Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 1 | /* |
| 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 Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 24 | #include <android-base/logging.h> |
| 25 | #include <android-base/strings.h> |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 26 | #include <bsdiff/bsdiff.h> |
| 27 | #include <gtest/gtest.h> |
| 28 | #include <openssl/sha.h> |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 29 | |
| 30 | #include "applypatch/applypatch_modes.h" |
| 31 | #include "common/test_constants.h" |
| 32 | #include "otautil/paths.h" |
| 33 | #include "otautil/print_sha1.h" |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 34 | #include "otautil/sysutil.h" |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 35 | |
| 36 | using namespace std::string_literals; |
| 37 | |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 38 | // Loads a given partition and returns a string of form "EMMC:name:size:hash". |
| 39 | static std::string GetEmmcTargetString(const std::string& filename, |
| 40 | const std::string& display_name = "") { |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 41 | std::string data; |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 42 | if (!android::base::ReadFileToString(filename, &data)) { |
| 43 | PLOG(ERROR) << "Failed to read " << filename; |
| 44 | return {}; |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 45 | } |
| 46 | |
| 47 | uint8_t digest[SHA_DIGEST_LENGTH]; |
| 48 | SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest); |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 49 | |
| 50 | return "EMMC:"s + (display_name.empty() ? filename : display_name) + ":" + |
| 51 | std::to_string(data.size()) + ":" + print_sha1(digest); |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 52 | } |
| 53 | |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 54 | class ApplyPatchModesTest : public ::testing::Test { |
| 55 | protected: |
| 56 | void SetUp() override { |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 57 | source = GetEmmcTargetString(from_testdata_base("boot.img")); |
| 58 | ASSERT_FALSE(source.empty()); |
| 59 | |
| 60 | std::string recovery_file = from_testdata_base("recovery.img"); |
| 61 | recovery = GetEmmcTargetString(recovery_file); |
| 62 | ASSERT_FALSE(recovery.empty()); |
| 63 | |
| 64 | ASSERT_TRUE(android::base::WriteStringToFile("", patched_file_.path)); |
| 65 | target = GetEmmcTargetString(recovery_file, patched_file_.path); |
| 66 | ASSERT_FALSE(target.empty()); |
| 67 | |
| 68 | Paths::Get().set_cache_temp_source(cache_source_.path); |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 69 | } |
| 70 | |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 71 | std::string source; |
| 72 | std::string target; |
| 73 | std::string recovery; |
| 74 | |
| 75 | private: |
| 76 | TemporaryFile cache_source_; |
| 77 | TemporaryFile patched_file_; |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 78 | }; |
| 79 | |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 80 | static int InvokeApplyPatchModes(const std::vector<std::string>& args) { |
| 81 | auto args_to_call = StringVectorToNullTerminatedArray(args); |
| 82 | return applypatch_modes(args_to_call.size() - 1, args_to_call.data()); |
| 83 | } |
| 84 | |
| 85 | static void VerifyPatchedTarget(const std::string& target) { |
| 86 | std::vector<std::string> pieces = android::base::Split(target, ":"); |
| 87 | ASSERT_EQ(4, pieces.size()); |
| 88 | ASSERT_EQ("EMMC", pieces[0]); |
| 89 | |
| 90 | std::string patched_emmc = GetEmmcTargetString(pieces[1]); |
| 91 | ASSERT_FALSE(patched_emmc.empty()); |
| 92 | ASSERT_EQ(target, patched_emmc); |
| 93 | } |
| 94 | |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 95 | TEST_F(ApplyPatchModesTest, InvalidArgs) { |
| 96 | // At least two args (including the filename). |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 97 | ASSERT_EQ(2, InvokeApplyPatchModes({ "applypatch" })); |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 98 | |
| 99 | // Unrecognized args. |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 100 | ASSERT_EQ(2, InvokeApplyPatchModes({ "applypatch", "-x" })); |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 101 | } |
| 102 | |
Kelvin Zhang | d77e7ea | 2021-01-08 23:21:30 -0500 | [diff] [blame] | 103 | TEST_F(ApplyPatchModesTest, DISABLED_PatchModeEmmcTarget) { |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 104 | std::vector<std::string> args{ |
| 105 | "applypatch", |
| 106 | "--bonus", |
| 107 | from_testdata_base("bonus.file"), |
| 108 | "--patch", |
| 109 | from_testdata_base("recovery-from-boot.p"), |
| 110 | "--target", |
| 111 | target, |
| 112 | "--source", |
| 113 | source, |
| 114 | }; |
| 115 | ASSERT_EQ(0, InvokeApplyPatchModes(args)); |
| 116 | VerifyPatchedTarget(target); |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 117 | } |
| 118 | |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 119 | // Tests patching an eMMC target without a separate bonus file (i.e. recovery-from-boot patch has |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 120 | // everything). |
Kelvin Zhang | d77e7ea | 2021-01-08 23:21:30 -0500 | [diff] [blame] | 121 | TEST_F(ApplyPatchModesTest, DISABLED_PatchModeEmmcTargetWithoutBonusFile) { |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 122 | std::vector<std::string> args{ |
| 123 | "applypatch", "--patch", from_testdata_base("recovery-from-boot-with-bonus.p"), |
| 124 | "--target", target, "--source", |
| 125 | source, |
| 126 | }; |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 127 | |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 128 | ASSERT_EQ(0, InvokeApplyPatchModes(args)); |
| 129 | VerifyPatchedTarget(target); |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 130 | } |
| 131 | |
| 132 | // Ensures that applypatch works with a bsdiff based recovery-from-boot.p. |
| 133 | TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithBsdiffPatch) { |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 134 | // Generate the bsdiff patch of recovery-from-boot.p. |
| 135 | std::string src_content; |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 136 | ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("boot.img"), &src_content)); |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 137 | |
| 138 | std::string tgt_content; |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 139 | ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("recovery.img"), &tgt_content)); |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 140 | |
| 141 | TemporaryFile patch_file; |
| 142 | ASSERT_EQ(0, |
| 143 | bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src_content.data()), src_content.size(), |
| 144 | reinterpret_cast<const uint8_t*>(tgt_content.data()), tgt_content.size(), |
| 145 | patch_file.path, nullptr)); |
| 146 | |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 147 | std::vector<std::string> args{ |
| 148 | "applypatch", "--patch", patch_file.path, "--target", target, "--source", source, |
| 149 | }; |
| 150 | ASSERT_EQ(0, InvokeApplyPatchModes(args)); |
| 151 | VerifyPatchedTarget(target); |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 152 | } |
| 153 | |
| 154 | TEST_F(ApplyPatchModesTest, PatchModeInvalidArgs) { |
| 155 | // Invalid bonus file. |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 156 | std::vector<std::string> args{ |
| 157 | "applypatch", "--bonus", "/doesntexist", "--patch", from_testdata_base("recovery-from-boot.p"), |
| 158 | "--target", target, "--source", source, |
| 159 | }; |
| 160 | ASSERT_NE(0, InvokeApplyPatchModes(args)); |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 161 | |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 162 | // With bonus file, but missing args. |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 163 | ASSERT_NE(0, |
| 164 | InvokeApplyPatchModes({ "applypatch", "--bonus", from_testdata_base("bonus.file") })); |
| 165 | } |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 166 | |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 167 | TEST_F(ApplyPatchModesTest, FlashMode) { |
| 168 | std::vector<std::string> args{ |
| 169 | "applypatch", "--flash", from_testdata_base("recovery.img"), "--target", target, |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 170 | }; |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 171 | ASSERT_EQ(0, InvokeApplyPatchModes(args)); |
| 172 | VerifyPatchedTarget(target); |
| 173 | } |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 174 | |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 175 | TEST_F(ApplyPatchModesTest, FlashModeInvalidArgs) { |
| 176 | std::vector<std::string> args{ |
| 177 | "applypatch", "--bonus", from_testdata_base("bonus.file"), "--flash", source, |
| 178 | "--target", target, |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 179 | }; |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 180 | ASSERT_NE(0, InvokeApplyPatchModes(args)); |
| 181 | } |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 182 | |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 183 | TEST_F(ApplyPatchModesTest, CheckMode) { |
| 184 | ASSERT_EQ(0, InvokeApplyPatchModes({ "applypatch", "--check", recovery })); |
| 185 | ASSERT_EQ(0, InvokeApplyPatchModes({ "applypatch", "--check", source })); |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | TEST_F(ApplyPatchModesTest, CheckModeInvalidArgs) { |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 189 | ASSERT_EQ(2, InvokeApplyPatchModes({ "applypatch", "--check" })); |
| 190 | } |
| 191 | |
| 192 | TEST_F(ApplyPatchModesTest, CheckModeNonEmmcTarget) { |
| 193 | ASSERT_NE(0, InvokeApplyPatchModes({ "applypatch", "--check", from_testdata_base("boot.img") })); |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 194 | } |
| 195 | |
| 196 | TEST_F(ApplyPatchModesTest, ShowLicenses) { |
Tao Bao | d34e6bc | 2018-07-13 13:11:09 -0700 | [diff] [blame] | 197 | ASSERT_EQ(0, InvokeApplyPatchModes({ "applypatch", "--license" })); |
Tao Bao | b8cb2e6 | 2018-07-06 12:14:36 -0700 | [diff] [blame] | 198 | } |