1 /*
2  * Copyright (C) 2019 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 <stdint.h>
18 #include <stdio.h>
19 
20 #include <map>
21 #include <string>
22 #include <vector>
23 
24 #include <android-base/file.h>
25 #include <android-base/stringprintf.h>
26 #include <android-base/strings.h>
27 #include <bsdiff/bsdiff.h>
28 #include <gtest/gtest.h>
29 #include <ziparchive/zip_writer.h>
30 
31 #include "otautil/paths.h"
32 #include "otautil/print_sha1.h"
33 #include "updater/blockimg.h"
34 #include "updater/build_info.h"
35 #include "updater/install.h"
36 #include "updater/simulator_runtime.h"
37 #include "updater/target_files.h"
38 #include "updater/updater.h"
39 
40 using std::string;
41 
42 // echo -n "system.img" > system.img && img2simg system.img sparse_system_string_.img 4096 &&
43 // hexdump -v -e '"    " 12/1 "0x%02x, " "\n"' sparse_system_string_.img
44 // The total size of the result sparse image is 4136 bytes; and we can append 0s in the end to get
45 // the full image.
46 constexpr uint8_t SPARSE_SYSTEM_HEADER[] = {
47   0x3a, 0xff, 0x26, 0xed, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x0c, 0x00, 0x00, 0x10, 0x00,
48   0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xca,
49   0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x10, 0x00, 0x00, 0x73, 0x79, 0x73, 0x74, 0x65,
50   0x6d, 0x2e, 0x69, 0x6d, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
51 };
52 
AddZipEntries(int fd,const std::map<string,string> & entries)53 static void AddZipEntries(int fd, const std::map<string, string>& entries) {
54   FILE* zip_file = fdopen(fd, "w");
55   ZipWriter writer(zip_file);
56   for (const auto& pair : entries) {
57     ASSERT_EQ(0, writer.StartEntry(pair.first.c_str(), 0));
58     ASSERT_EQ(0, writer.WriteBytes(pair.second.data(), pair.second.size()));
59     ASSERT_EQ(0, writer.FinishEntry());
60   }
61   ASSERT_EQ(0, writer.Finish());
62   ASSERT_EQ(0, fclose(zip_file));
63 }
64 
CalculateSha1(const string & data)65 static string CalculateSha1(const string& data) {
66   uint8_t digest[SHA_DIGEST_LENGTH];
67   SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest);
68   return print_sha1(digest);
69 }
70 
CreateBsdiffPatch(const string & src,const string & tgt,string * patch)71 static void CreateBsdiffPatch(const string& src, const string& tgt, string* patch) {
72   TemporaryFile patch_file;
73   ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src.data()), src.size(),
74                               reinterpret_cast<const uint8_t*>(tgt.data()), tgt.size(),
75                               patch_file.path, nullptr));
76   ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, patch));
77 }
78 
RunSimulation(std::string_view src_tf,std::string_view ota_package,bool expected)79 static void RunSimulation(std::string_view src_tf, std::string_view ota_package, bool expected) {
80   TemporaryFile cmd_pipe;
81   TemporaryFile temp_saved_source;
82   TemporaryFile temp_last_command;
83   TemporaryDir temp_stash_base;
84 
85   Paths::Get().set_cache_temp_source(temp_saved_source.path);
86   Paths::Get().set_last_command_file(temp_last_command.path);
87   Paths::Get().set_stash_directory_base(temp_stash_base.path);
88 
89   // Configure edify's functions.
90   RegisterBuiltins();
91   RegisterInstallFunctions();
92   RegisterBlockImageFunctions();
93 
94   // Run the update simulation and check the result.
95   TemporaryDir work_dir;
96   BuildInfo build_info(work_dir.path, false);
97   ASSERT_TRUE(build_info.ParseTargetFile(src_tf, false));
98   Updater updater(std::make_unique<SimulatorRuntime>(&build_info));
99   ASSERT_TRUE(updater.Init(cmd_pipe.release(), ota_package, false));
100   ASSERT_EQ(expected, updater.RunUpdate());
101   // TODO(xunchang) check the recovery&system has the expected contents.
102 }
103 
104 class UpdateSimulatorTest : public ::testing::Test {
105  protected:
SetUp()106   void SetUp() override {
107     std::vector<string> props = {
108       "import /oem/oem.prop oem*",
109       "# begin build properties",
110       "# autogenerated by buildinfo.sh",
111       "ro.build.id=OPR1.170510.001",
112       "ro.build.display.id=OPR1.170510.001 dev-keys",
113       "ro.build.version.incremental=3993052",
114       "ro.build.version.release=O",
115       "ro.build.date=Wed May 10 11:10:29 UTC 2017",
116       "ro.build.date.utc=1494414629",
117       "ro.build.type=user",
118       "ro.build.tags=dev-keys",
119       "ro.build.flavor=angler-user",
120       "ro.product.system.brand=google",
121       "ro.product.system.name=angler",
122       "ro.product.system.device=angler",
123     };
124     build_prop_string_ = android::base::Join(props, "\n");
125 
126     fstab_content_ = R"(
127 #<src>         <mnt_point>  <type>  <mnt_flags and options> <fs_mgr_flags>
128 # More comments.....
129 
130 /dev/block/by-name/system     /system     ext4    ro,barrier=1    wait
131 /dev/block/by-name/vendor     /vendor     ext4    ro              wait,verify=/dev/metadata
132 /dev/block/by-name/cache      /cache      ext4    noatime,errors=panic      wait,check
133 /dev/block/by-name/modem      /firmware   vfat    ro,uid=1000,gid=1000,     wait
134 /dev/block/by-name/boot       /boot       emmc    defaults        defaults
135 /dev/block/by-name/recovery   /recovery   emmc    defaults        defaults
136 /dev/block/by-name/misc       /misc       emmc    defaults
137 /dev/block/by-name/modem      /modem      emmc    defaults        defaults)";
138 
139     raw_system_string_ = "system.img" + string(4086, '\0');  // raw image is 4096 bytes in total
140     sparse_system_string_ = string(SPARSE_SYSTEM_HEADER, std::end(SPARSE_SYSTEM_HEADER)) +
141                             string(4136 - sizeof(SPARSE_SYSTEM_HEADER), '\0');
142   }
143 
144   string build_prop_string_;
145   string fstab_content_;
146   string raw_system_string_;
147   string sparse_system_string_;
148 };
149 
TEST_F(UpdateSimulatorTest,TargetFile_ExtractImage)150 TEST_F(UpdateSimulatorTest, TargetFile_ExtractImage) {
151   TemporaryFile zip_file;
152   AddZipEntries(zip_file.release(), { { "META/misc_info.txt", "extfs_sparse_flag=-s" },
153                                       { "IMAGES/system.img", sparse_system_string_ } });
154   TargetFile target_file(zip_file.path, false);
155   ASSERT_TRUE(target_file.Open());
156 
157   TemporaryDir temp_dir;
158   TemporaryFile raw_image;
159   ASSERT_TRUE(target_file.ExtractImage(
160       "IMAGES/system.img", FstabInfo("/dev/system", "system", "ext4"), temp_dir.path, &raw_image));
161 
162   // Check the raw image has expected contents.
163   string content;
164   ASSERT_TRUE(android::base::ReadFileToString(raw_image.path, &content));
165   string expected_content = "system.img" + string(4086, '\0');
166   ASSERT_EQ(expected_content, content);
167 }
168 
TEST_F(UpdateSimulatorTest,TargetFile_ParseFstabInfo)169 TEST_F(UpdateSimulatorTest, TargetFile_ParseFstabInfo) {
170   TemporaryFile zip_file;
171   AddZipEntries(zip_file.release(),
172                 { { "META/misc_info.txt", "" },
173                   { "RECOVERY/RAMDISK/system/etc/recovery.fstab", fstab_content_ } });
174   TargetFile target_file(zip_file.path, false);
175   ASSERT_TRUE(target_file.Open());
176 
177   std::vector<FstabInfo> fstab_info;
178   EXPECT_TRUE(target_file.ParseFstabInfo(&fstab_info));
179 
180   std::vector<std::vector<string>> transformed;
181   std::transform(fstab_info.begin(), fstab_info.end(), std::back_inserter(transformed),
182                  [](const FstabInfo& info) {
183                    return std::vector<string>{ info.blockdev_name, info.mount_point, info.fs_type };
184                  });
185 
186   std::vector<std::vector<string>> expected = {
187     { "/dev/block/by-name/system", "/system", "ext4" },
188     { "/dev/block/by-name/vendor", "/vendor", "ext4" },
189     { "/dev/block/by-name/cache", "/cache", "ext4" },
190     { "/dev/block/by-name/boot", "/boot", "emmc" },
191     { "/dev/block/by-name/recovery", "/recovery", "emmc" },
192     { "/dev/block/by-name/misc", "/misc", "emmc" },
193     { "/dev/block/by-name/modem", "/modem", "emmc" },
194   };
195   EXPECT_EQ(expected, transformed);
196 }
197 
TEST_F(UpdateSimulatorTest,BuildInfo_ParseTargetFile)198 TEST_F(UpdateSimulatorTest, BuildInfo_ParseTargetFile) {
199   std::map<string, string> entries = {
200     { "META/misc_info.txt", "" },
201     { "SYSTEM/build.prop", build_prop_string_ },
202     { "RECOVERY/RAMDISK/system/etc/recovery.fstab", fstab_content_ },
203     { "IMAGES/recovery.img", "" },
204     { "IMAGES/boot.img", "" },
205     { "IMAGES/misc.img", "" },
206     { "IMAGES/system.map", "" },
207     { "IMAGES/system.img", sparse_system_string_ },
208   };
209 
210   TemporaryFile zip_file;
211   AddZipEntries(zip_file.release(), entries);
212 
213   TemporaryDir temp_dir;
214   BuildInfo build_info(temp_dir.path, false);
215   ASSERT_TRUE(build_info.ParseTargetFile(zip_file.path, false));
216 
217   std::map<string, string> expected_result = {
218     { "ro.build.id", "OPR1.170510.001" },
219     { "ro.build.display.id", "OPR1.170510.001 dev-keys" },
220     { "ro.build.version.incremental", "3993052" },
221     { "ro.build.version.release", "O" },
222     { "ro.build.date", "Wed May 10 11:10:29 UTC 2017" },
223     { "ro.build.date.utc", "1494414629" },
224     { "ro.build.type", "user" },
225     { "ro.build.tags", "dev-keys" },
226     { "ro.build.flavor", "angler-user" },
227     { "ro.product.brand", "google" },
228     { "ro.product.name", "angler" },
229     { "ro.product.device", "angler" },
230   };
231 
232   for (const auto& [key, value] : expected_result) {
233     ASSERT_EQ(value, build_info.GetProperty(key, ""));
234   }
235 
236   // Check that the temp files for each block device are created successfully.
237   for (auto name : { "/dev/block/by-name/system", "/dev/block/by-name/recovery",
238                      "/dev/block/by-name/boot", "/dev/block/by-name/misc" }) {
239     ASSERT_EQ(0, access(build_info.FindBlockDeviceName(name).c_str(), R_OK));
240   }
241 }
242 
TEST_F(UpdateSimulatorTest,RunUpdateSmoke)243 TEST_F(UpdateSimulatorTest, RunUpdateSmoke) {
244   string recovery_img_string = "recovery.img";
245   string boot_img_string = "boot.img";
246 
247   std::map<string, string> src_entries{
248     { "META/misc_info.txt", "extfs_sparse_flag=-s" },
249     { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ },
250     { "SYSTEM/build.prop", build_prop_string_ },
251     { "IMAGES/recovery.img", "" },
252     { "IMAGES/boot.img", boot_img_string },
253     { "IMAGES/system.img", sparse_system_string_ },
254   };
255 
256   // Construct the source target-files.
257   TemporaryFile src_tf;
258   AddZipEntries(src_tf.release(), src_entries);
259 
260   string recovery_from_boot;
261   CreateBsdiffPatch(boot_img_string, recovery_img_string, &recovery_from_boot);
262 
263   // Set up the apply patch commands to patch the recovery image.
264   string recovery_sha1 = CalculateSha1(recovery_img_string);
265   string boot_sha1 = CalculateSha1(boot_img_string);
266   string apply_patch_source_string = android::base::StringPrintf(
267       "EMMC:/dev/block/by-name/boot:%zu:%s", boot_img_string.size(), boot_sha1.c_str());
268   string apply_patch_target_string = android::base::StringPrintf(
269       "EMMC:/dev/block/by-name/recovery:%zu:%s", recovery_img_string.size(), recovery_sha1.c_str());
270   string check_command = android::base::StringPrintf(
271       R"(patch_partition_check("%s", "%s") || abort("check failed");)",
272       apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
273   string patch_command = android::base::StringPrintf(
274       R"(patch_partition("%s", "%s", package_extract_file("patch.p")) || abort("patch failed");)",
275       apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
276 
277   // Add the commands to update the system image. Test common commands:
278   // * getprop
279   // * ui_print
280   // * patch_partition
281   // * package_extract_file (single argument)
282   // * block_image_verify, block_image_update
283   string tgt_system_string = string(4096, 'a');
284   string system_patch;
285   CreateBsdiffPatch(raw_system_string_, tgt_system_string, &system_patch);
286 
287   string tgt_system_hash = CalculateSha1(tgt_system_string);
288   string src_system_hash = CalculateSha1(raw_system_string_);
289 
290   std::vector<std::string> transfer_list = {
291     "4",
292     "1",
293     "0",
294     "0",
295     android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,1 1 2,0,1", system_patch.size(),
296                                 src_system_hash.c_str(), tgt_system_hash.c_str()),
297   };
298 
299   // Construct the updater_script.
300   std::vector<string> updater_commands = {
301     R"(getprop("ro.product.device") == "angler" || abort("This package is for \"angler\"");)",
302     R"(ui_print("Source: angler/OPR1.170510.001");)",
303     check_command,
304     patch_command,
305     R"(block_image_verify("/dev/block/by-name/system", )"
306     R"(package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") || )"
307     R"(abort("Failed to verify system.");)",
308     R"(block_image_update("/dev/block/by-name/system", )"
309     R"(package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") || )"
310     R"(abort("Failed to verify system.");)",
311   };
312   string updater_script = android::base::Join(updater_commands, '\n');
313 
314   // Construct the ota update package.
315   std::map<string, string> ota_entries{
316     { "system.new.dat", "" },
317     { "system.patch.dat", system_patch },
318     { "system.transfer.list", android::base::Join(transfer_list, '\n') },
319     { "META-INF/com/google/android/updater-script", updater_script },
320     { "patch.p", recovery_from_boot },
321   };
322 
323   TemporaryFile ota_package;
324   AddZipEntries(ota_package.release(), ota_entries);
325 
326   RunSimulation(src_tf.path, ota_package.path, true);
327 }
328 
329 TEST_F(UpdateSimulatorTest, RunUpdateUnrecognizedFunction) {
330   std::map<string, string> src_entries{
331     { "META/misc_info.txt", "extfs_sparse_flag=-s" },
332     { "IMAGES/system.img", sparse_system_string_ },
333     { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ },
334     { "SYSTEM/build.prop", build_prop_string_ },
335   };
336 
337   TemporaryFile src_tf;
338   AddZipEntries(src_tf.release(), src_entries);
339 
340   std::map<string, string> ota_entries{
341     { "system.new.dat", "" },
342     { "system.patch.dat", "" },
343     { "system.transfer.list", "" },
344     { "META-INF/com/google/android/updater-script", R"(bad_function("");)" },
345   };
346 
347   TemporaryFile ota_package;
348   AddZipEntries(ota_package.release(), ota_entries);
349 
350   RunSimulation(src_tf.path, ota_package.path, false);
351 }
352 
353 TEST_F(UpdateSimulatorTest, RunUpdateApplyPatchFailed) {
354   string recovery_img_string = "recovery.img";
355   string boot_img_string = "boot.img";
356 
357   std::map<string, string> src_entries{
358     { "META/misc_info.txt", "extfs_sparse_flag=-s" },
359     { "IMAGES/recovery.img", "" },
360     { "IMAGES/boot.img", boot_img_string },
361     { "IMAGES/system.img", sparse_system_string_ },
362     { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ },
363     { "SYSTEM/build.prop", build_prop_string_ },
364   };
365 
366   TemporaryFile src_tf;
367   AddZipEntries(src_tf.release(), src_entries);
368 
369   string recovery_sha1 = CalculateSha1(recovery_img_string);
370   string boot_sha1 = CalculateSha1(boot_img_string);
371   string apply_patch_source_string = android::base::StringPrintf(
372       "EMMC:/dev/block/by-name/boot:%zu:%s", boot_img_string.size(), boot_sha1.c_str());
373   string apply_patch_target_string = android::base::StringPrintf(
374       "EMMC:/dev/block/by-name/recovery:%zu:%s", recovery_img_string.size(), recovery_sha1.c_str());
375   string check_command = android::base::StringPrintf(
376       R"(patch_partition_check("%s", "%s") || abort("check failed");)",
377       apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
378   string patch_command = android::base::StringPrintf(
379       R"(patch_partition("%s", "%s", package_extract_file("patch.p")) || abort("failed");)",
380       apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
381 
382   // Give an invalid recovery patch and expect the apply patch to fail.
383   // TODO(xunchang) check the cause code.
384   std::vector<string> updater_commands = {
385     R"(ui_print("Source: angler/OPR1.170510.001");)",
386     check_command,
387     patch_command,
388   };
389 
390   string updater_script = android::base::Join(updater_commands, '\n');
391   std::map<string, string> ota_entries{
392     { "system.new.dat", "" },
393     { "system.patch.dat", "" },
394     { "system.transfer.list", "" },
395     { "META-INF/com/google/android/updater-script", updater_script },
396     { "patch.p", "random string" },
397   };
398 
399   TemporaryFile ota_package;
400   AddZipEntries(ota_package.release(), ota_entries);
401 
402   RunSimulation(src_tf.path, ota_package.path, false);
403 }
404