diff options
Diffstat (limited to 'tests/component')
-rw-r--r-- | tests/component/applypatch_modes_test.cpp | 198 | ||||
-rw-r--r-- | tests/component/bootloader_message_test.cpp | 116 | ||||
-rw-r--r-- | tests/component/edify_test.cpp | 167 | ||||
-rw-r--r-- | tests/component/imgdiff_test.cpp | 1114 | ||||
-rw-r--r-- | tests/component/install_test.cpp | 598 | ||||
-rw-r--r-- | tests/component/resources_test.cpp | 120 | ||||
-rw-r--r-- | tests/component/sideload_test.cpp | 108 | ||||
-rw-r--r-- | tests/component/uncrypt_test.cpp | 198 | ||||
-rw-r--r-- | tests/component/update_verifier_test.cpp | 219 | ||||
-rw-r--r-- | tests/component/updater_test.cpp | 1225 | ||||
-rw-r--r-- | tests/component/verifier_test.cpp | 364 |
11 files changed, 0 insertions, 4427 deletions
diff --git a/tests/component/applypatch_modes_test.cpp b/tests/component/applypatch_modes_test.cpp deleted file mode 100644 index 08414b796..000000000 --- a/tests/component/applypatch_modes_test.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agree to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <stdlib.h> - -#include <string> -#include <vector> - -#include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/strings.h> -#include <bsdiff/bsdiff.h> -#include <gtest/gtest.h> -#include <openssl/sha.h> - -#include "applypatch/applypatch_modes.h" -#include "common/test_constants.h" -#include "otautil/paths.h" -#include "otautil/print_sha1.h" -#include "otautil/sysutil.h" - -using namespace std::string_literals; - -// Loads a given partition and returns a string of form "EMMC:name:size:hash". -static std::string GetEmmcTargetString(const std::string& filename, - const std::string& display_name = "") { - std::string data; - if (!android::base::ReadFileToString(filename, &data)) { - PLOG(ERROR) << "Failed to read " << filename; - return {}; - } - - uint8_t digest[SHA_DIGEST_LENGTH]; - SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest); - - return "EMMC:"s + (display_name.empty() ? filename : display_name) + ":" + - std::to_string(data.size()) + ":" + print_sha1(digest); -} - -class ApplyPatchModesTest : public ::testing::Test { - protected: - void SetUp() override { - source = GetEmmcTargetString(from_testdata_base("boot.img")); - ASSERT_FALSE(source.empty()); - - std::string recovery_file = from_testdata_base("recovery.img"); - recovery = GetEmmcTargetString(recovery_file); - ASSERT_FALSE(recovery.empty()); - - ASSERT_TRUE(android::base::WriteStringToFile("", patched_file_.path)); - target = GetEmmcTargetString(recovery_file, patched_file_.path); - ASSERT_FALSE(target.empty()); - - Paths::Get().set_cache_temp_source(cache_source_.path); - } - - std::string source; - std::string target; - std::string recovery; - - private: - TemporaryFile cache_source_; - TemporaryFile patched_file_; -}; - -static int InvokeApplyPatchModes(const std::vector<std::string>& args) { - auto args_to_call = StringVectorToNullTerminatedArray(args); - return applypatch_modes(args_to_call.size() - 1, args_to_call.data()); -} - -static void VerifyPatchedTarget(const std::string& target) { - std::vector<std::string> pieces = android::base::Split(target, ":"); - ASSERT_EQ(4, pieces.size()); - ASSERT_EQ("EMMC", pieces[0]); - - std::string patched_emmc = GetEmmcTargetString(pieces[1]); - ASSERT_FALSE(patched_emmc.empty()); - ASSERT_EQ(target, patched_emmc); -} - -TEST_F(ApplyPatchModesTest, InvalidArgs) { - // At least two args (including the filename). - ASSERT_EQ(2, InvokeApplyPatchModes({ "applypatch" })); - - // Unrecognized args. - ASSERT_EQ(2, InvokeApplyPatchModes({ "applypatch", "-x" })); -} - -TEST_F(ApplyPatchModesTest, PatchModeEmmcTarget) { - std::vector<std::string> args{ - "applypatch", - "--bonus", - from_testdata_base("bonus.file"), - "--patch", - from_testdata_base("recovery-from-boot.p"), - "--target", - target, - "--source", - source, - }; - ASSERT_EQ(0, InvokeApplyPatchModes(args)); - VerifyPatchedTarget(target); -} - -// Tests patching an eMMC target without a separate bonus file (i.e. recovery-from-boot patch has -// everything). -TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithoutBonusFile) { - std::vector<std::string> args{ - "applypatch", "--patch", from_testdata_base("recovery-from-boot-with-bonus.p"), - "--target", target, "--source", - source, - }; - - ASSERT_EQ(0, InvokeApplyPatchModes(args)); - VerifyPatchedTarget(target); -} - -// Ensures that applypatch works with a bsdiff based recovery-from-boot.p. -TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithBsdiffPatch) { - // Generate the bsdiff patch of recovery-from-boot.p. - std::string src_content; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("boot.img"), &src_content)); - - std::string tgt_content; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("recovery.img"), &tgt_content)); - - TemporaryFile patch_file; - ASSERT_EQ(0, - bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src_content.data()), src_content.size(), - reinterpret_cast<const uint8_t*>(tgt_content.data()), tgt_content.size(), - patch_file.path, nullptr)); - - std::vector<std::string> args{ - "applypatch", "--patch", patch_file.path, "--target", target, "--source", source, - }; - ASSERT_EQ(0, InvokeApplyPatchModes(args)); - VerifyPatchedTarget(target); -} - -TEST_F(ApplyPatchModesTest, PatchModeInvalidArgs) { - // Invalid bonus file. - std::vector<std::string> args{ - "applypatch", "--bonus", "/doesntexist", "--patch", from_testdata_base("recovery-from-boot.p"), - "--target", target, "--source", source, - }; - ASSERT_NE(0, InvokeApplyPatchModes(args)); - - // With bonus file, but missing args. - ASSERT_NE(0, - InvokeApplyPatchModes({ "applypatch", "--bonus", from_testdata_base("bonus.file") })); -} - -TEST_F(ApplyPatchModesTest, FlashMode) { - std::vector<std::string> args{ - "applypatch", "--flash", from_testdata_base("recovery.img"), "--target", target, - }; - ASSERT_EQ(0, InvokeApplyPatchModes(args)); - VerifyPatchedTarget(target); -} - -TEST_F(ApplyPatchModesTest, FlashModeInvalidArgs) { - std::vector<std::string> args{ - "applypatch", "--bonus", from_testdata_base("bonus.file"), "--flash", source, - "--target", target, - }; - ASSERT_NE(0, InvokeApplyPatchModes(args)); -} - -TEST_F(ApplyPatchModesTest, CheckMode) { - ASSERT_EQ(0, InvokeApplyPatchModes({ "applypatch", "--check", recovery })); - ASSERT_EQ(0, InvokeApplyPatchModes({ "applypatch", "--check", source })); -} - -TEST_F(ApplyPatchModesTest, CheckModeInvalidArgs) { - ASSERT_EQ(2, InvokeApplyPatchModes({ "applypatch", "--check" })); -} - -TEST_F(ApplyPatchModesTest, CheckModeNonEmmcTarget) { - ASSERT_NE(0, InvokeApplyPatchModes({ "applypatch", "--check", from_testdata_base("boot.img") })); -} - -TEST_F(ApplyPatchModesTest, ShowLicenses) { - ASSERT_EQ(0, InvokeApplyPatchModes({ "applypatch", "--license" })); -} diff --git a/tests/component/bootloader_message_test.cpp b/tests/component/bootloader_message_test.cpp deleted file mode 100644 index b005d199c..000000000 --- a/tests/component/bootloader_message_test.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <string> -#include <vector> - -#include <android-base/file.h> -#include <android-base/strings.h> -#include <bootloader_message/bootloader_message.h> -#include <gtest/gtest.h> - -TEST(BootloaderMessageTest, read_and_write_bootloader_message) { - TemporaryFile temp_misc; - - // Write the BCB. - bootloader_message boot = {}; - strlcpy(boot.command, "command", sizeof(boot.command)); - strlcpy(boot.recovery, "message1\nmessage2\n", sizeof(boot.recovery)); - strlcpy(boot.status, "status1", sizeof(boot.status)); - - std::string err; - ASSERT_TRUE(write_bootloader_message_to(boot, temp_misc.path, &err)) - << "Failed to write BCB: " << err; - - // Read and verify. - bootloader_message boot_verify; - ASSERT_TRUE(read_bootloader_message_from(&boot_verify, temp_misc.path, &err)) - << "Failed to read BCB: " << err; - - ASSERT_EQ(std::string(reinterpret_cast<const char*>(&boot), sizeof(boot)), - std::string(reinterpret_cast<const char*>(&boot_verify), sizeof(boot_verify))); -} - -TEST(BootloaderMessageTest, update_bootloader_message_in_struct) { - // Write the options to BCB. - std::vector<std::string> options = { "option1", "option2" }; - - bootloader_message boot = {}; - // Inject some bytes into boot. - strlcpy(boot.recovery, "random message", sizeof(boot.recovery)); - strlcpy(boot.status, "status bytes", sizeof(boot.status)); - strlcpy(boot.stage, "stage bytes", sizeof(boot.stage)); - strlcpy(boot.reserved, "reserved bytes", sizeof(boot.reserved)); - - ASSERT_TRUE(update_bootloader_message_in_struct(&boot, options)); - - // Verify that command and recovery fields should be set. - ASSERT_EQ("boot-recovery", std::string(boot.command)); - std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n"; - ASSERT_EQ(expected, std::string(boot.recovery)); - - // The rest should be intact. - ASSERT_EQ("status bytes", std::string(boot.status)); - ASSERT_EQ("stage bytes", std::string(boot.stage)); - ASSERT_EQ("reserved bytes", std::string(boot.reserved)); -} - -TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_empty) { - // Write empty vector. - std::vector<std::string> options; - - // Read and verify. - bootloader_message boot = {}; - ASSERT_TRUE(update_bootloader_message_in_struct(&boot, options)); - - // command and recovery fields should be set. - ASSERT_EQ("boot-recovery", std::string(boot.command)); - ASSERT_EQ("recovery\n", std::string(boot.recovery)); - - // The rest should be empty. - ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status))); - ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage))); - ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'), - std::string(boot.reserved, sizeof(boot.reserved))); -} - -TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_long) { - // Write super long message. - std::vector<std::string> options; - for (int i = 0; i < 100; i++) { - options.push_back("option: " + std::to_string(i)); - } - - // Read and verify. - bootloader_message boot = {}; - ASSERT_TRUE(update_bootloader_message_in_struct(&boot, options)); - - // Make sure it's long enough. - std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n"; - ASSERT_GE(expected.size(), sizeof(boot.recovery)); - - // command and recovery fields should be set. - ASSERT_EQ("boot-recovery", std::string(boot.command)); - ASSERT_EQ(expected.substr(0, sizeof(boot.recovery) - 1), std::string(boot.recovery)); - ASSERT_EQ('\0', boot.recovery[sizeof(boot.recovery) - 1]); - - // The rest should be empty. - ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status))); - ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage))); - ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'), - std::string(boot.reserved, sizeof(boot.reserved))); -} - diff --git a/tests/component/edify_test.cpp b/tests/component/edify_test.cpp deleted file mode 100644 index 8397bd38e..000000000 --- a/tests/component/edify_test.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <memory> -#include <string> - -#include <gtest/gtest.h> - -#include "edify/expr.h" - -static void expect(const std::string& expr_str, const char* expected) { - std::unique_ptr<Expr> e; - int error_count = 0; - EXPECT_EQ(0, ParseString(expr_str, &e, &error_count)); - EXPECT_EQ(0, error_count); - - State state(expr_str, nullptr); - - std::string result; - bool status = Evaluate(&state, e, &result); - - if (expected == nullptr) { - EXPECT_FALSE(status); - } else { - EXPECT_STREQ(expected, result.c_str()); - } -} - -class EdifyTest : public ::testing::Test { - protected: - void SetUp() { - RegisterBuiltins(); - } -}; - -TEST_F(EdifyTest, parsing) { - expect("a", "a"); - expect("\"a\"", "a"); - expect("\"\\x61\"", "a"); - expect("# this is a comment\n" - " a\n" - " \n", - "a"); -} - -TEST_F(EdifyTest, sequence) { - // sequence operator - expect("a; b; c", "c"); -} - -TEST_F(EdifyTest, concat) { - // string concat operator - expect("a + b", "ab"); - expect("a + \n \"b\"", "ab"); - expect("a + b +\nc\n", "abc"); - - // string concat function - expect("concat(a, b)", "ab"); - expect("concat(a,\n \"b\")", "ab"); - expect("concat(a + b,\nc,\"d\")", "abcd"); - expect("\"concat\"(a + b,\nc,\"d\")", "abcd"); -} - -TEST_F(EdifyTest, logical) { - // logical and - expect("a && b", "b"); - expect("a && \"\"", ""); - expect("\"\" && b", ""); - expect("\"\" && \"\"", ""); - expect("\"\" && abort()", ""); // test short-circuiting - expect("t && abort()", nullptr); - - // logical or - expect("a || b", "a"); - expect("a || \"\"", "a"); - expect("\"\" || b", "b"); - expect("\"\" || \"\"", ""); - expect("a || abort()", "a"); // test short-circuiting - expect("\"\" || abort()", NULL); - - // logical not - expect("!a", ""); - expect("! \"\"", "t"); - expect("!!a", "t"); -} - -TEST_F(EdifyTest, precedence) { - // precedence - expect("\"\" == \"\" && b", "b"); - expect("a + b == ab", "t"); - expect("ab == a + b", "t"); - expect("a + (b == ab)", "a"); - expect("(ab == a) + b", "b"); -} - -TEST_F(EdifyTest, substring) { - // substring function - expect("is_substring(cad, abracadabra)", "t"); - expect("is_substring(abrac, abracadabra)", "t"); - expect("is_substring(dabra, abracadabra)", "t"); - expect("is_substring(cad, abracxadabra)", ""); - expect("is_substring(abrac, axbracadabra)", ""); - expect("is_substring(dabra, abracadabrxa)", ""); -} - -TEST_F(EdifyTest, ifelse) { - // ifelse function - expect("ifelse(t, yes, no)", "yes"); - expect("ifelse(!t, yes, no)", "no"); - expect("ifelse(t, yes, abort())", "yes"); - expect("ifelse(!t, abort(), no)", "no"); -} - -TEST_F(EdifyTest, if_statement) { - // if "statements" - expect("if t then yes else no endif", "yes"); - expect("if \"\" then yes else no endif", "no"); - expect("if \"\" then yes endif", ""); - expect("if \"\"; t then yes endif", "yes"); -} - -TEST_F(EdifyTest, comparison) { - // numeric comparisons - expect("less_than_int(3, 14)", "t"); - expect("less_than_int(14, 3)", ""); - expect("less_than_int(x, 3)", ""); - expect("less_than_int(3, x)", ""); - expect("greater_than_int(3, 14)", ""); - expect("greater_than_int(14, 3)", "t"); - expect("greater_than_int(x, 3)", ""); - expect("greater_than_int(3, x)", ""); -} - -TEST_F(EdifyTest, big_string) { - expect(std::string(8192, 's'), std::string(8192, 's').c_str()); -} - -TEST_F(EdifyTest, unknown_function) { - const char* script1 = "unknown_function()"; - std::unique_ptr<Expr> expr; - int error_count = 0; - EXPECT_EQ(1, ParseString(script1, &expr, &error_count)); - EXPECT_EQ(1, error_count); - - const char* script2 = "abc; unknown_function()"; - error_count = 0; - EXPECT_EQ(1, ParseString(script2, &expr, &error_count)); - EXPECT_EQ(1, error_count); - - const char* script3 = "unknown_function1() || yes"; - error_count = 0; - EXPECT_EQ(1, ParseString(script3, &expr, &error_count)); - EXPECT_EQ(1, error_count); -} diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp deleted file mode 100644 index e76ccbdfb..000000000 --- a/tests/component/imgdiff_test.cpp +++ /dev/null @@ -1,1114 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> - -#include <algorithm> -#include <string> -#include <tuple> -#include <vector> - -#include <android-base/file.h> -#include <android-base/memory.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> -#include <applypatch/imgdiff.h> -#include <applypatch/imgdiff_image.h> -#include <applypatch/imgpatch.h> -#include <gtest/gtest.h> -#include <ziparchive/zip_writer.h> - -#include "common/test_constants.h" - -using android::base::get_unaligned; - -// Sanity check for the given imgdiff patch header. -static void verify_patch_header(const std::string& patch, size_t* num_normal, size_t* num_raw, - size_t* num_deflate) { - const size_t size = patch.size(); - const char* data = patch.data(); - - ASSERT_GE(size, 12U); - ASSERT_EQ("IMGDIFF2", std::string(data, 8)); - - const int num_chunks = get_unaligned<int32_t>(data + 8); - ASSERT_GE(num_chunks, 0); - - size_t normal = 0; - size_t raw = 0; - size_t deflate = 0; - - size_t pos = 12; - for (int i = 0; i < num_chunks; ++i) { - ASSERT_LE(pos + 4, size); - int type = get_unaligned<int32_t>(data + pos); - pos += 4; - if (type == CHUNK_NORMAL) { - pos += 24; - ASSERT_LE(pos, size); - normal++; - } else if (type == CHUNK_RAW) { - ASSERT_LE(pos + 4, size); - ssize_t data_len = get_unaligned<int32_t>(data + pos); - ASSERT_GT(data_len, 0); - pos += 4 + data_len; - ASSERT_LE(pos, size); - raw++; - } else if (type == CHUNK_DEFLATE) { - pos += 60; - ASSERT_LE(pos, size); - deflate++; - } else { - FAIL() << "Invalid patch type: " << type; - } - } - - if (num_normal != nullptr) *num_normal = normal; - if (num_raw != nullptr) *num_raw = raw; - if (num_deflate != nullptr) *num_deflate = deflate; -} - -static void GenerateTarget(const std::string& src, const std::string& patch, std::string* patched) { - patched->clear(); - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(), - reinterpret_cast<const unsigned char*>(patch.data()), patch.size(), - [&](const unsigned char* data, size_t len) { - patched->append(reinterpret_cast<const char*>(data), len); - return len; - })); -} - -static void verify_patched_image(const std::string& src, const std::string& patch, - const std::string& tgt) { - std::string patched; - GenerateTarget(src, patch, &patched); - ASSERT_EQ(tgt, patched); -} - -TEST(ImgdiffTest, invalid_args) { - // Insufficient inputs. - ASSERT_EQ(2, imgdiff(1, (const char* []){ "imgdiff" })); - ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-z" })); - ASSERT_EQ(2, imgdiff(2, (const char* []){ "imgdiff", "-b" })); - ASSERT_EQ(2, imgdiff(3, (const char* []){ "imgdiff", "-z", "-b" })); - - // Failed to read bonus file. - ASSERT_EQ(1, imgdiff(3, (const char* []){ "imgdiff", "-b", "doesntexist" })); - - // Failed to read input files. - ASSERT_EQ(1, imgdiff(4, (const char* []){ "imgdiff", "doesntexist", "doesntexist", "output" })); - ASSERT_EQ( - 1, imgdiff(5, (const char* []){ "imgdiff", "-z", "doesntexist", "doesntexist", "output" })); -} - -TEST(ImgdiffTest, image_mode_smoke) { - // Random bytes. - const std::string src("abcdefg"); - TemporaryFile src_file; - ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path)); - - const std::string tgt("abcdefgxyz"); - TemporaryFile tgt_file; - ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path)); - - TemporaryFile patch_file; - std::vector<const char*> args = { - "imgdiff", src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - // Verify. - std::string patch; - ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); - - // Expect one CHUNK_RAW entry. - size_t num_normal; - size_t num_raw; - size_t num_deflate; - verify_patch_header(patch, &num_normal, &num_raw, &num_deflate); - ASSERT_EQ(0U, num_normal); - ASSERT_EQ(0U, num_deflate); - ASSERT_EQ(1U, num_raw); - - verify_patched_image(src, patch, tgt); -} - -TEST(ImgdiffTest, zip_mode_smoke_store) { - // Construct src and tgt zip files. - TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.release(), "wb"); - ZipWriter src_writer(src_file_ptr); - ASSERT_EQ(0, src_writer.StartEntry("file1.txt", 0)); // Store mode. - const std::string src_content("abcdefg"); - ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size())); - ASSERT_EQ(0, src_writer.FinishEntry()); - ASSERT_EQ(0, src_writer.Finish()); - ASSERT_EQ(0, fclose(src_file_ptr)); - - TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); - ZipWriter tgt_writer(tgt_file_ptr); - ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", 0)); // Store mode. - const std::string tgt_content("abcdefgxyz"); - ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size())); - ASSERT_EQ(0, tgt_writer.FinishEntry()); - ASSERT_EQ(0, tgt_writer.Finish()); - ASSERT_EQ(0, fclose(tgt_file_ptr)); - - // Compute patch. - TemporaryFile patch_file; - std::vector<const char*> args = { - "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - // Verify. - std::string tgt; - ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - std::string src; - ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src)); - std::string patch; - ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); - - // Expect one CHUNK_RAW entry. - size_t num_normal; - size_t num_raw; - size_t num_deflate; - verify_patch_header(patch, &num_normal, &num_raw, &num_deflate); - ASSERT_EQ(0U, num_normal); - ASSERT_EQ(0U, num_deflate); - ASSERT_EQ(1U, num_raw); - - verify_patched_image(src, patch, tgt); -} - -TEST(ImgdiffTest, zip_mode_smoke_compressed) { - // Generate 1 block of random data. - std::string random_data; - random_data.reserve(4096); - generate_n(back_inserter(random_data), 4096, []() { return rand() % 256; }); - - // Construct src and tgt zip files. - TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.release(), "wb"); - ZipWriter src_writer(src_file_ptr); - ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress)); - const std::string src_content = random_data; - ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size())); - ASSERT_EQ(0, src_writer.FinishEntry()); - ASSERT_EQ(0, src_writer.Finish()); - ASSERT_EQ(0, fclose(src_file_ptr)); - - TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); - ZipWriter tgt_writer(tgt_file_ptr); - ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress)); - const std::string tgt_content = random_data + "extra contents"; - ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size())); - ASSERT_EQ(0, tgt_writer.FinishEntry()); - ASSERT_EQ(0, tgt_writer.Finish()); - ASSERT_EQ(0, fclose(tgt_file_ptr)); - - // Compute patch. - TemporaryFile patch_file; - std::vector<const char*> args = { - "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - // Verify. - std::string tgt; - ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - std::string src; - ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src)); - std::string patch; - ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); - - // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer). - size_t num_normal; - size_t num_raw; - size_t num_deflate; - verify_patch_header(patch, &num_normal, &num_raw, &num_deflate); - ASSERT_EQ(0U, num_normal); - ASSERT_EQ(1U, num_deflate); - ASSERT_EQ(2U, num_raw); - - verify_patched_image(src, patch, tgt); -} - -TEST(ImgdiffTest, zip_mode_empty_target) { - TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.release(), "wb"); - ZipWriter src_writer(src_file_ptr); - ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress)); - const std::string src_content = "abcdefg"; - ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size())); - ASSERT_EQ(0, src_writer.FinishEntry()); - ASSERT_EQ(0, src_writer.Finish()); - ASSERT_EQ(0, fclose(src_file_ptr)); - - // Construct a empty entry in the target zip. - TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); - ZipWriter tgt_writer(tgt_file_ptr); - ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress)); - const std::string tgt_content; - ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size())); - ASSERT_EQ(0, tgt_writer.FinishEntry()); - ASSERT_EQ(0, tgt_writer.Finish()); - - // Compute patch. - TemporaryFile patch_file; - std::vector<const char*> args = { - "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - // Verify. - std::string tgt; - ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - std::string src; - ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src)); - std::string patch; - ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); - - verify_patched_image(src, patch, tgt); -} - -TEST(ImgdiffTest, zip_mode_smoke_trailer_zeros) { - // Generate 1 block of random data. - std::string random_data; - random_data.reserve(4096); - generate_n(back_inserter(random_data), 4096, []() { return rand() % 256; }); - - // Construct src and tgt zip files. - TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.release(), "wb"); - ZipWriter src_writer(src_file_ptr); - ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress)); - const std::string src_content = random_data; - ASSERT_EQ(0, src_writer.WriteBytes(src_content.data(), src_content.size())); - ASSERT_EQ(0, src_writer.FinishEntry()); - ASSERT_EQ(0, src_writer.Finish()); - ASSERT_EQ(0, fclose(src_file_ptr)); - - TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); - ZipWriter tgt_writer(tgt_file_ptr); - ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress)); - const std::string tgt_content = random_data + "abcdefg"; - ASSERT_EQ(0, tgt_writer.WriteBytes(tgt_content.data(), tgt_content.size())); - ASSERT_EQ(0, tgt_writer.FinishEntry()); - ASSERT_EQ(0, tgt_writer.Finish()); - // Add trailing zeros to the target zip file. - std::vector<uint8_t> zeros(10); - ASSERT_EQ(zeros.size(), fwrite(zeros.data(), sizeof(uint8_t), zeros.size(), tgt_file_ptr)); - ASSERT_EQ(0, fclose(tgt_file_ptr)); - - // Compute patch. - TemporaryFile patch_file; - std::vector<const char*> args = { - "imgdiff", "-z", src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - // Verify. - std::string tgt; - ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - std::string src; - ASSERT_TRUE(android::base::ReadFileToString(src_file.path, &src)); - std::string patch; - ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); - - // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer). - size_t num_normal; - size_t num_raw; - size_t num_deflate; - verify_patch_header(patch, &num_normal, &num_raw, &num_deflate); - ASSERT_EQ(0U, num_normal); - ASSERT_EQ(1U, num_deflate); - ASSERT_EQ(2U, num_raw); - - verify_patched_image(src, patch, tgt); -} - -TEST(ImgdiffTest, image_mode_simple) { - std::string gzipped_source_path = from_testdata_base("gzipped_source"); - std::string gzipped_source; - ASSERT_TRUE(android::base::ReadFileToString(gzipped_source_path, &gzipped_source)); - - const std::string src = "abcdefg" + gzipped_source; - TemporaryFile src_file; - ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path)); - - std::string gzipped_target_path = from_testdata_base("gzipped_target"); - std::string gzipped_target; - ASSERT_TRUE(android::base::ReadFileToString(gzipped_target_path, &gzipped_target)); - const std::string tgt = "abcdefgxyz" + gzipped_target; - - TemporaryFile tgt_file; - ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path)); - - TemporaryFile patch_file; - std::vector<const char*> args = { - "imgdiff", src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - // Verify. - std::string patch; - ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); - - // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer). - size_t num_normal; - size_t num_raw; - size_t num_deflate; - verify_patch_header(patch, &num_normal, &num_raw, &num_deflate); - ASSERT_EQ(0U, num_normal); - ASSERT_EQ(1U, num_deflate); - ASSERT_EQ(2U, num_raw); - - verify_patched_image(src, patch, tgt); -} - -TEST(ImgdiffTest, image_mode_bad_gzip) { - // Modify the uncompressed length in the gzip footer. - const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e', - '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac', - '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03', - '\xff', '\xff', '\xff' }; - const std::string src(src_data.cbegin(), src_data.cend()); - TemporaryFile src_file; - ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path)); - - // Modify the uncompressed length in the gzip footer. - const std::vector<char> tgt_data = { - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b', - '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac', - '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\xff', '\xff', '\xff' - }; - const std::string tgt(tgt_data.cbegin(), tgt_data.cend()); - TemporaryFile tgt_file; - ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path)); - - TemporaryFile patch_file; - std::vector<const char*> args = { - "imgdiff", src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - // Verify. - std::string patch; - ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); - verify_patched_image(src, patch, tgt); -} - -TEST(ImgdiffTest, image_mode_different_num_chunks) { - // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd) + gzipped "test". - const std::vector<char> src_data = { - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', '\x1f', '\x8b', '\x08', - '\x00', '\xc4', '\x1e', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac', '\x02', - '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03', '\x00', '\x00', '\x00', '\x1f', '\x8b', - '\x08', '\x00', '\xb2', '\x3a', '\x53', '\x58', '\x00', '\x03', '\x2b', '\x49', '\x2d', - '\x2e', '\x01', '\x00', '\x0c', '\x7e', '\x7f', '\xd8', '\x04', '\x00', '\x00', '\x00' - }; - const std::string src(src_data.cbegin(), src_data.cend()); - TemporaryFile src_file; - ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path)); - - // tgt: "abcdefgxyz" + gzipped "xxyyzz". - const std::vector<char> tgt_data = { - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b', - '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac', - '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00' - }; - const std::string tgt(tgt_data.cbegin(), tgt_data.cend()); - TemporaryFile tgt_file; - ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path)); - - TemporaryFile patch_file; - std::vector<const char*> args = { - "imgdiff", src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(1, imgdiff(args.size(), args.data())); -} - -TEST(ImgdiffTest, image_mode_merge_chunks) { - // src: "abcdefg" + gzipped_source. - std::string gzipped_source_path = from_testdata_base("gzipped_source"); - std::string gzipped_source; - ASSERT_TRUE(android::base::ReadFileToString(gzipped_source_path, &gzipped_source)); - - const std::string src = "abcdefg" + gzipped_source; - TemporaryFile src_file; - ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path)); - - // tgt: gzipped_target + "abcdefgxyz". - std::string gzipped_target_path = from_testdata_base("gzipped_target"); - std::string gzipped_target; - ASSERT_TRUE(android::base::ReadFileToString(gzipped_target_path, &gzipped_target)); - - const std::string tgt = gzipped_target + "abcdefgxyz"; - TemporaryFile tgt_file; - ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path)); - - // Since a gzipped entry will become CHUNK_RAW (header) + CHUNK_DEFLATE (data) + - // CHUNK_RAW (footer), they both should contain the same chunk types after merging. - - TemporaryFile patch_file; - std::vector<const char*> args = { - "imgdiff", src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - // Verify. - std::string patch; - ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); - - // Expect three entries: CHUNK_RAW (header) + CHUNK_DEFLATE (data) + CHUNK_RAW (footer). - size_t num_normal; - size_t num_raw; - size_t num_deflate; - verify_patch_header(patch, &num_normal, &num_raw, &num_deflate); - ASSERT_EQ(0U, num_normal); - ASSERT_EQ(1U, num_deflate); - ASSERT_EQ(2U, num_raw); - - verify_patched_image(src, patch, tgt); -} - -TEST(ImgdiffTest, image_mode_spurious_magic) { - // src: "abcdefgh" + '0x1f8b0b00' + some bytes. - const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e', - '\x53', '\x58', 't', 'e', 's', 't' }; - const std::string src(src_data.cbegin(), src_data.cend()); - TemporaryFile src_file; - ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path)); - - // tgt: "abcdefgxyz". - const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' }; - const std::string tgt(tgt_data.cbegin(), tgt_data.cend()); - TemporaryFile tgt_file; - ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path)); - - TemporaryFile patch_file; - std::vector<const char*> args = { - "imgdiff", src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - // Verify. - std::string patch; - ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); - - // Expect one CHUNK_RAW (header) entry. - size_t num_normal; - size_t num_raw; - size_t num_deflate; - verify_patch_header(patch, &num_normal, &num_raw, &num_deflate); - ASSERT_EQ(0U, num_normal); - ASSERT_EQ(0U, num_deflate); - ASSERT_EQ(1U, num_raw); - - verify_patched_image(src, patch, tgt); -} - -TEST(ImgdiffTest, image_mode_short_input1) { - // src: "abcdefgh" + '0x1f8b0b'. - const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', '\x1f', '\x8b', '\x08' }; - const std::string src(src_data.cbegin(), src_data.cend()); - TemporaryFile src_file; - ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path)); - - // tgt: "abcdefgxyz". - const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' }; - const std::string tgt(tgt_data.cbegin(), tgt_data.cend()); - TemporaryFile tgt_file; - ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path)); - - TemporaryFile patch_file; - std::vector<const char*> args = { - "imgdiff", src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - // Verify. - std::string patch; - ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); - - // Expect one CHUNK_RAW (header) entry. - size_t num_normal; - size_t num_raw; - size_t num_deflate; - verify_patch_header(patch, &num_normal, &num_raw, &num_deflate); - ASSERT_EQ(0U, num_normal); - ASSERT_EQ(0U, num_deflate); - ASSERT_EQ(1U, num_raw); - - verify_patched_image(src, patch, tgt); -} - -TEST(ImgdiffTest, image_mode_short_input2) { - // src: "abcdefgh" + '0x1f8b0b00'. - const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', '\x1f', '\x8b', '\x08', '\x00' }; - const std::string src(src_data.cbegin(), src_data.cend()); - TemporaryFile src_file; - ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path)); - - // tgt: "abcdefgxyz". - const std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' }; - const std::string tgt(tgt_data.cbegin(), tgt_data.cend()); - TemporaryFile tgt_file; - ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path)); - - TemporaryFile patch_file; - std::vector<const char*> args = { - "imgdiff", src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - // Verify. - std::string patch; - ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); - - // Expect one CHUNK_RAW (header) entry. - size_t num_normal; - size_t num_raw; - size_t num_deflate; - verify_patch_header(patch, &num_normal, &num_raw, &num_deflate); - ASSERT_EQ(0U, num_normal); - ASSERT_EQ(0U, num_deflate); - ASSERT_EQ(1U, num_raw); - - verify_patched_image(src, patch, tgt); -} - -TEST(ImgdiffTest, image_mode_single_entry_long) { - // src: "abcdefgh" + '0x1f8b0b00' + some bytes. - const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e', - '\x53', '\x58', 't', 'e', 's', 't' }; - const std::string src(src_data.cbegin(), src_data.cend()); - TemporaryFile src_file; - ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path)); - - // tgt: "abcdefgxyz" + 200 bytes. - std::vector<char> tgt_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z' }; - tgt_data.resize(tgt_data.size() + 200); - - const std::string tgt(tgt_data.cbegin(), tgt_data.cend()); - TemporaryFile tgt_file; - ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path)); - - TemporaryFile patch_file; - std::vector<const char*> args = { - "imgdiff", src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - // Verify. - std::string patch; - ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); - - // Expect one CHUNK_NORMAL entry, since it's exceeding the 160-byte limit for RAW. - size_t num_normal; - size_t num_raw; - size_t num_deflate; - verify_patch_header(patch, &num_normal, &num_raw, &num_deflate); - ASSERT_EQ(1U, num_normal); - ASSERT_EQ(0U, num_deflate); - ASSERT_EQ(0U, num_raw); - - verify_patched_image(src, patch, tgt); -} - -TEST(ImgpatchTest, image_mode_patch_corruption) { - // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd). - const std::vector<char> src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', - 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e', - '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac', - '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03', - '\x00', '\x00', '\x00' }; - const std::string src(src_data.cbegin(), src_data.cend()); - TemporaryFile src_file; - ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path)); - - // tgt: "abcdefgxyz" + gzipped "xxyyzz". - const std::vector<char> tgt_data = { - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b', - '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac', - '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00' - }; - const std::string tgt(tgt_data.cbegin(), tgt_data.cend()); - TemporaryFile tgt_file; - ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path)); - - TemporaryFile patch_file; - std::vector<const char*> args = { - "imgdiff", src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - // Verify. - std::string patch; - ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); - verify_patched_image(src, patch, tgt); - - // Corrupt the end of the patch and expect the ApplyImagePatch to fail. - patch.insert(patch.end() - 10, 10, '0'); - ASSERT_EQ(-1, ApplyImagePatch(reinterpret_cast<const unsigned char*>(src.data()), src.size(), - reinterpret_cast<const unsigned char*>(patch.data()), patch.size(), - [](const unsigned char* /*data*/, size_t len) { return len; })); -} - -static void construct_store_entry(const std::vector<std::tuple<std::string, size_t, char>>& info, - ZipWriter* writer) { - for (auto& t : info) { - // Create t(1) blocks of t(2), and write the data to t(0) - ASSERT_EQ(0, writer->StartEntry(std::get<0>(t).c_str(), 0)); - const std::string content(std::get<1>(t) * 4096, std::get<2>(t)); - ASSERT_EQ(0, writer->WriteBytes(content.data(), content.size())); - ASSERT_EQ(0, writer->FinishEntry()); - } -} - -static void construct_deflate_entry(const std::vector<std::tuple<std::string, size_t, size_t>>& info, - ZipWriter* writer, const std::string& data) { - for (auto& t : info) { - // t(0): entry_name; t(1): block offset; t(2) length in blocks. - ASSERT_EQ(0, writer->StartEntry(std::get<0>(t).c_str(), ZipWriter::kCompress)); - ASSERT_EQ(0, writer->WriteBytes(data.data() + std::get<1>(t) * 4096, std::get<2>(t) * 4096)); - ASSERT_EQ(0, writer->FinishEntry()); - } -} - -// Look for the source and patch pieces in debug_dir. Generate a target piece from each pair. -// Concatenate all the target pieces and match against the orignal one. Used pieces in debug_dir -// will be cleaned up. -static void GenerateAndCheckSplitTarget(const std::string& debug_dir, size_t count, - const std::string& tgt) { - std::string patched; - for (size_t i = 0; i < count; i++) { - std::string split_src_path = android::base::StringPrintf("%s/src-%zu", debug_dir.c_str(), i); - std::string split_src; - ASSERT_TRUE(android::base::ReadFileToString(split_src_path, &split_src)); - ASSERT_EQ(0, unlink(split_src_path.c_str())); - - std::string split_patch_path = - android::base::StringPrintf("%s/patch-%zu", debug_dir.c_str(), i); - std::string split_patch; - ASSERT_TRUE(android::base::ReadFileToString(split_patch_path, &split_patch)); - ASSERT_EQ(0, unlink(split_patch_path.c_str())); - - std::string split_tgt; - GenerateTarget(split_src, split_patch, &split_tgt); - patched += split_tgt; - } - - // Verify we can get back the original target image. - ASSERT_EQ(tgt, patched); -} - -std::vector<ImageChunk> ConstructImageChunks( - const std::vector<uint8_t>& content, const std::vector<std::tuple<std::string, size_t>>& info) { - std::vector<ImageChunk> chunks; - size_t start = 0; - for (const auto& t : info) { - size_t length = std::get<1>(t); - chunks.emplace_back(CHUNK_NORMAL, start, &content, length, std::get<0>(t)); - start += length; - } - - return chunks; -} - -TEST(ImgdiffTest, zip_mode_split_image_smoke) { - std::vector<uint8_t> content; - content.reserve(4096 * 50); - uint8_t n = 0; - generate_n(back_inserter(content), 4096 * 50, [&n]() { return n++ / 4096; }); - - ZipModeImage tgt_image(false, 4096 * 10); - std::vector<ImageChunk> tgt_chunks = ConstructImageChunks(content, { { "a", 100 }, - { "b", 4096 * 2 }, - { "c", 4096 * 3 }, - { "d", 300 }, - { "e-0", 4096 * 10 }, - { "e-1", 4096 * 5 }, - { "CD", 200 } }); - tgt_image.Initialize(std::move(tgt_chunks), - std::vector<uint8_t>(content.begin(), content.begin() + 82520)); - - tgt_image.DumpChunks(); - - ZipModeImage src_image(true, 4096 * 10); - std::vector<ImageChunk> src_chunks = ConstructImageChunks(content, { { "b", 4096 * 3 }, - { "c-0", 4096 * 10 }, - { "c-1", 4096 * 2 }, - { "a", 4096 * 5 }, - { "e-0", 4096 * 10 }, - { "e-1", 10000 }, - { "CD", 5000 } }); - src_image.Initialize(std::move(src_chunks), - std::vector<uint8_t>(content.begin(), content.begin() + 137880)); - - std::vector<ZipModeImage> split_tgt_images; - std::vector<ZipModeImage> split_src_images; - std::vector<SortedRangeSet> split_src_ranges; - - ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images, - &split_src_images, &split_src_ranges); - - // src_piece 1: a 5 blocks, b 3 blocks - // src_piece 2: c-0 10 blocks - // src_piece 3: d 0 block, e-0 10 blocks - // src_piece 4: e-1 2 blocks; CD 2 blocks - ASSERT_EQ(split_tgt_images.size(), split_src_images.size()); - ASSERT_EQ(static_cast<size_t>(4), split_tgt_images.size()); - - ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[0].NumOfChunks()); - ASSERT_EQ(static_cast<size_t>(12288), split_tgt_images[0][0].DataLengthForPatch()); - ASSERT_EQ("4,0,3,15,20", split_src_ranges[0].ToString()); - - ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[1].NumOfChunks()); - ASSERT_EQ(static_cast<size_t>(12288), split_tgt_images[1][0].DataLengthForPatch()); - ASSERT_EQ("2,3,13", split_src_ranges[1].ToString()); - - ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[2].NumOfChunks()); - ASSERT_EQ(static_cast<size_t>(40960), split_tgt_images[2][0].DataLengthForPatch()); - ASSERT_EQ("2,20,30", split_src_ranges[2].ToString()); - - ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[3].NumOfChunks()); - ASSERT_EQ(static_cast<size_t>(16984), split_tgt_images[3][0].DataLengthForPatch()); - ASSERT_EQ("2,30,34", split_src_ranges[3].ToString()); -} - -TEST(ImgdiffTest, zip_mode_store_large_apk) { - // Construct src and tgt zip files with limit = 10 blocks. - // src tgt - // 12 blocks 'd' 3 blocks 'a' - // 8 blocks 'c' 3 blocks 'b' - // 3 blocks 'b' 8 blocks 'c' (exceeds limit) - // 3 blocks 'a' 12 blocks 'd' (exceeds limit) - // 3 blocks 'e' - TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); - ZipWriter tgt_writer(tgt_file_ptr); - construct_store_entry( - { { "a", 3, 'a' }, { "b", 3, 'b' }, { "c", 8, 'c' }, { "d", 12, 'd' }, { "e", 3, 'e' } }, - &tgt_writer); - ASSERT_EQ(0, tgt_writer.Finish()); - ASSERT_EQ(0, fclose(tgt_file_ptr)); - - TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.release(), "wb"); - ZipWriter src_writer(src_file_ptr); - construct_store_entry({ { "d", 12, 'd' }, { "c", 8, 'c' }, { "b", 3, 'b' }, { "a", 3, 'a' } }, - &src_writer); - ASSERT_EQ(0, src_writer.Finish()); - ASSERT_EQ(0, fclose(src_file_ptr)); - - // Compute patch. - TemporaryFile patch_file; - TemporaryFile split_info_file; - TemporaryDir debug_dir; - std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path); - std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); - std::vector<const char*> args = { - "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(), - src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - std::string tgt; - ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - - // Expect 4 pieces of patch. (Roughly 3'a',3'b'; 8'c'; 10'd'; 2'd'3'e') - GenerateAndCheckSplitTarget(debug_dir.path, 4, tgt); -} - -TEST(ImgdiffTest, zip_mode_deflate_large_apk) { - // Src and tgt zip files are constructed as follows. - // src tgt - // 22 blocks, "d" 4 blocks, "a" - // 5 blocks, "b" 4 blocks, "b" - // 3 blocks, "a" 8 blocks, "c" (exceeds limit) - // 1 block, "g" 20 blocks, "d" (exceeds limit) - // 8 blocks, "c" 2 blocks, "e" - // 1 block, "f" 1 block , "f" - std::string tgt_path = from_testdata_base("deflate_tgt.zip"); - std::string src_path = from_testdata_base("deflate_src.zip"); - - ZipModeImage src_image(true, 10 * 4096); - ZipModeImage tgt_image(false, 10 * 4096); - ASSERT_TRUE(src_image.Initialize(src_path)); - ASSERT_TRUE(tgt_image.Initialize(tgt_path)); - ASSERT_TRUE(ZipModeImage::CheckAndProcessChunks(&tgt_image, &src_image)); - - src_image.DumpChunks(); - tgt_image.DumpChunks(); - - std::vector<ZipModeImage> split_tgt_images; - std::vector<ZipModeImage> split_src_images; - std::vector<SortedRangeSet> split_src_ranges; - ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images, - &split_src_images, &split_src_ranges); - - // Expected split images with limit = 10 blocks. - // src_piece 0: a 3 blocks, b 5 blocks - // src_piece 1: c 8 blocks - // src_piece 2: d-0 10 block - // src_piece 3: d-1 10 blocks - // src_piece 4: e 1 block, CD - ASSERT_EQ(split_tgt_images.size(), split_src_images.size()); - ASSERT_EQ(static_cast<size_t>(5), split_tgt_images.size()); - - ASSERT_EQ(static_cast<size_t>(2), split_src_images[0].NumOfChunks()); - ASSERT_EQ("a", split_src_images[0][0].GetEntryName()); - ASSERT_EQ("b", split_src_images[0][1].GetEntryName()); - - ASSERT_EQ(static_cast<size_t>(1), split_src_images[1].NumOfChunks()); - ASSERT_EQ("c", split_src_images[1][0].GetEntryName()); - - ASSERT_EQ(static_cast<size_t>(0), split_src_images[2].NumOfChunks()); - ASSERT_EQ(static_cast<size_t>(0), split_src_images[3].NumOfChunks()); - ASSERT_EQ(static_cast<size_t>(0), split_src_images[4].NumOfChunks()); - - // Compute patch. - TemporaryFile patch_file; - TemporaryFile split_info_file; - TemporaryDir debug_dir; - ASSERT_TRUE(ZipModeImage::GeneratePatches(split_tgt_images, split_src_images, split_src_ranges, - patch_file.path, split_info_file.path, debug_dir.path)); - - // Verify the content of split info. - // Expect 5 pieces of patch. ["a","b"; "c"; "d-0"; "d-1"; "e"] - std::string split_info_string; - android::base::ReadFileToString(split_info_file.path, &split_info_string); - std::vector<std::string> info_list = - android::base::Split(android::base::Trim(split_info_string), "\n"); - - ASSERT_EQ(static_cast<size_t>(7), info_list.size()); - ASSERT_EQ("2", android::base::Trim(info_list[0])); - ASSERT_EQ("5", android::base::Trim(info_list[1])); - - std::string tgt; - ASSERT_TRUE(android::base::ReadFileToString(tgt_path, &tgt)); - ASSERT_EQ(static_cast<size_t>(160385), tgt.size()); - std::vector<std::string> tgt_file_ranges = { - "36864 2,22,31", "32768 2,31,40", "40960 2,0,11", "40960 2,11,21", "8833 4,21,22,40,41", - }; - - for (size_t i = 0; i < 5; i++) { - struct stat st; - std::string path = android::base::StringPrintf("%s/patch-%zu", debug_dir.path, i); - ASSERT_EQ(0, stat(path.c_str(), &st)); - ASSERT_EQ(std::to_string(st.st_size) + " " + tgt_file_ranges[i], - android::base::Trim(info_list[i + 2])); - } - - GenerateAndCheckSplitTarget(debug_dir.path, 5, tgt); -} - -TEST(ImgdiffTest, zip_mode_no_match_source) { - // Generate 20 blocks of random data. - std::string random_data; - random_data.reserve(4096 * 20); - generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; }); - - TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); - ZipWriter tgt_writer(tgt_file_ptr); - - construct_deflate_entry({ { "a", 0, 4 }, { "b", 5, 5 }, { "c", 11, 5 } }, &tgt_writer, - random_data); - - ASSERT_EQ(0, tgt_writer.Finish()); - ASSERT_EQ(0, fclose(tgt_file_ptr)); - - // We don't have a matching source entry. - TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.release(), "wb"); - ZipWriter src_writer(src_file_ptr); - construct_store_entry({ { "d", 1, 'd' } }, &src_writer); - ASSERT_EQ(0, src_writer.Finish()); - ASSERT_EQ(0, fclose(src_file_ptr)); - - // Compute patch. - TemporaryFile patch_file; - TemporaryFile split_info_file; - TemporaryDir debug_dir; - std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path); - std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); - std::vector<const char*> args = { - "imgdiff", "-z", "--block-limit=10", debug_dir_arg.c_str(), split_info_arg.c_str(), - src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - std::string tgt; - ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - - // Expect 1 pieces of patch due to no matching source entry. - GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt); -} - -TEST(ImgdiffTest, zip_mode_large_enough_limit) { - // Generate 20 blocks of random data. - std::string random_data; - random_data.reserve(4096 * 20); - generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; }); - - TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); - ZipWriter tgt_writer(tgt_file_ptr); - - construct_deflate_entry({ { "a", 0, 10 }, { "b", 10, 5 } }, &tgt_writer, random_data); - - ASSERT_EQ(0, tgt_writer.Finish()); - ASSERT_EQ(0, fclose(tgt_file_ptr)); - - // Construct 10 blocks of source. - TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.release(), "wb"); - ZipWriter src_writer(src_file_ptr); - construct_deflate_entry({ { "a", 1, 10 } }, &src_writer, random_data); - ASSERT_EQ(0, src_writer.Finish()); - ASSERT_EQ(0, fclose(src_file_ptr)); - - // Compute patch with a limit of 20 blocks. - TemporaryFile patch_file; - TemporaryFile split_info_file; - TemporaryDir debug_dir; - std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path); - std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); - std::vector<const char*> args = { - "imgdiff", "-z", "--block-limit=20", split_info_arg.c_str(), debug_dir_arg.c_str(), - src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - std::string tgt; - ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - - // Expect 1 piece of patch since limit is larger than the zip file size. - GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt); -} - -TEST(ImgdiffTest, zip_mode_large_apk_small_target_chunk) { - TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); - ZipWriter tgt_writer(tgt_file_ptr); - - // The first entry is less than 4096 bytes, followed immediately by an entry that has a very - // large counterpart in the source file. Therefore the first entry will be patched separately. - std::string small_chunk("a", 2000); - ASSERT_EQ(0, tgt_writer.StartEntry("a", 0)); - ASSERT_EQ(0, tgt_writer.WriteBytes(small_chunk.data(), small_chunk.size())); - ASSERT_EQ(0, tgt_writer.FinishEntry()); - construct_store_entry( - { - { "b", 12, 'b' }, { "c", 3, 'c' }, - }, - &tgt_writer); - ASSERT_EQ(0, tgt_writer.Finish()); - ASSERT_EQ(0, fclose(tgt_file_ptr)); - - TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.release(), "wb"); - ZipWriter src_writer(src_file_ptr); - construct_store_entry({ { "a", 1, 'a' }, { "b", 13, 'b' }, { "c", 1, 'c' } }, &src_writer); - ASSERT_EQ(0, src_writer.Finish()); - ASSERT_EQ(0, fclose(src_file_ptr)); - - // Compute patch. - TemporaryFile patch_file; - TemporaryFile split_info_file; - TemporaryDir debug_dir; - std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path); - std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); - std::vector<const char*> args = { - "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(), - src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - std::string tgt; - ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - - // Expect three split src images: - // src_piece 0: a 1 blocks - // src_piece 1: b-0 10 blocks - // src_piece 2: b-1 3 blocks, c 1 blocks, CD - GenerateAndCheckSplitTarget(debug_dir.path, 3, tgt); -} - -TEST(ImgdiffTest, zip_mode_large_apk_skipped_small_target_chunk) { - TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); - ZipWriter tgt_writer(tgt_file_ptr); - - construct_store_entry( - { - { "a", 11, 'a' }, - }, - &tgt_writer); - - // Construct a tiny target entry of 1 byte, which will be skipped due to the tail alignment of - // the previous entry. - std::string small_chunk("b", 1); - ASSERT_EQ(0, tgt_writer.StartEntry("b", 0)); - ASSERT_EQ(0, tgt_writer.WriteBytes(small_chunk.data(), small_chunk.size())); - ASSERT_EQ(0, tgt_writer.FinishEntry()); - - ASSERT_EQ(0, tgt_writer.Finish()); - ASSERT_EQ(0, fclose(tgt_file_ptr)); - - TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.release(), "wb"); - ZipWriter src_writer(src_file_ptr); - construct_store_entry( - { - { "a", 11, 'a' }, { "b", 11, 'b' }, - }, - &src_writer); - ASSERT_EQ(0, src_writer.Finish()); - ASSERT_EQ(0, fclose(src_file_ptr)); - - // Compute patch. - TemporaryFile patch_file; - TemporaryFile split_info_file; - TemporaryDir debug_dir; - std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path); - std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); - std::vector<const char*> args = { - "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(), - src_file.path, tgt_file.path, patch_file.path, - }; - ASSERT_EQ(0, imgdiff(args.size(), args.data())); - - std::string tgt; - ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - - // Expect two split src images: - // src_piece 0: a-0 10 blocks - // src_piece 1: a-0 1 block, CD - GenerateAndCheckSplitTarget(debug_dir.path, 2, tgt); -} diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp deleted file mode 100644 index 36820f837..000000000 --- a/tests/component/install_test.cpp +++ /dev/null @@ -1,598 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agree to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include <algorithm> -#include <random> -#include <string> -#include <vector> - -#include <android-base/file.h> -#include <android-base/properties.h> -#include <android-base/strings.h> -#include <gtest/gtest.h> -#include <vintf/VintfObjectRecovery.h> -#include <ziparchive/zip_archive.h> -#include <ziparchive/zip_writer.h> - -#include "install/install.h" -#include "install/wipe_device.h" -#include "otautil/paths.h" -#include "private/setup_commands.h" - -static void BuildZipArchive(const std::map<std::string, std::string>& file_map, int fd, - int compression_type) { - FILE* zip_file = fdopen(fd, "w"); - ZipWriter writer(zip_file); - for (const auto& [name, content] : file_map) { - ASSERT_EQ(0, writer.StartEntry(name.c_str(), compression_type)); - ASSERT_EQ(0, writer.WriteBytes(content.data(), content.size())); - ASSERT_EQ(0, writer.FinishEntry()); - } - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); -} - -TEST(InstallTest, verify_package_compatibility_no_entry) { - TemporaryFile temp_file; - // The archive must have something to be opened correctly. - BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored); - - // Doesn't contain compatibility zip entry. - ZipArchiveHandle zip; - ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - ASSERT_TRUE(verify_package_compatibility(zip)); - CloseArchive(zip); -} - -TEST(InstallTest, verify_package_compatibility_invalid_entry) { - TemporaryFile temp_file; - BuildZipArchive({ { "compatibility.zip", "" } }, temp_file.release(), kCompressStored); - - // Empty compatibility zip entry. - ZipArchiveHandle zip; - ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - ASSERT_FALSE(verify_package_compatibility(zip)); - CloseArchive(zip); -} - -TEST(InstallTest, read_metadata_from_package_smoke) { - TemporaryFile temp_file; - const std::string content("abc=defg"); - BuildZipArchive({ { "META-INF/com/android/metadata", content } }, temp_file.release(), - kCompressStored); - - ZipArchiveHandle zip; - ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - std::map<std::string, std::string> metadata; - ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); - ASSERT_EQ("defg", metadata["abc"]); - CloseArchive(zip); - - TemporaryFile temp_file2; - BuildZipArchive({ { "META-INF/com/android/metadata", content } }, temp_file2.release(), - kCompressDeflated); - - ASSERT_EQ(0, OpenArchive(temp_file2.path, &zip)); - metadata.clear(); - ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); - ASSERT_EQ("defg", metadata["abc"]); - CloseArchive(zip); -} - -TEST(InstallTest, read_metadata_from_package_no_entry) { - TemporaryFile temp_file; - BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored); - - ZipArchiveHandle zip; - ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - std::map<std::string, std::string> metadata; - ASSERT_FALSE(ReadMetadataFromPackage(zip, &metadata)); - CloseArchive(zip); -} - -TEST(InstallTest, read_wipe_ab_partition_list) { - std::vector<std::string> partition_list = { - "/dev/block/bootdevice/by-name/system_a", "/dev/block/bootdevice/by-name/system_b", - "/dev/block/bootdevice/by-name/vendor_a", "/dev/block/bootdevice/by-name/vendor_b", - "/dev/block/bootdevice/by-name/userdata", "# Wipe the boot partitions last", - "/dev/block/bootdevice/by-name/boot_a", "/dev/block/bootdevice/by-name/boot_b", - }; - TemporaryFile temp_file; - BuildZipArchive({ { "recovery.wipe", android::base::Join(partition_list, '\n') } }, - temp_file.release(), kCompressDeflated); - std::string wipe_package; - ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &wipe_package)); - - auto package = Package::CreateMemoryPackage( - std::vector<uint8_t>(wipe_package.begin(), wipe_package.end()), nullptr); - - auto read_partition_list = GetWipePartitionList(package.get()); - std::vector<std::string> expected = { - "/dev/block/bootdevice/by-name/system_a", "/dev/block/bootdevice/by-name/system_b", - "/dev/block/bootdevice/by-name/vendor_a", "/dev/block/bootdevice/by-name/vendor_b", - "/dev/block/bootdevice/by-name/userdata", "/dev/block/bootdevice/by-name/boot_a", - "/dev/block/bootdevice/by-name/boot_b", - }; - ASSERT_EQ(expected, read_partition_list); -} - -TEST(InstallTest, verify_package_compatibility_with_libvintf_malformed_xml) { - TemporaryFile compatibility_zip_file; - std::string malformed_xml = "malformed"; - BuildZipArchive({ { "system_manifest.xml", malformed_xml } }, compatibility_zip_file.release(), - kCompressDeflated); - - TemporaryFile temp_file; - std::string compatibility_zip_content; - ASSERT_TRUE( - android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content)); - BuildZipArchive({ { "compatibility.zip", compatibility_zip_content } }, temp_file.release(), - kCompressStored); - - ZipArchiveHandle zip; - ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - std::vector<std::string> compatibility_info; - compatibility_info.push_back(malformed_xml); - // Malformed compatibility zip is expected to be rejected by libvintf. But we defer that to - // libvintf. - std::string err; - bool result = - android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err) == 0; - ASSERT_EQ(result, verify_package_compatibility(zip)); - CloseArchive(zip); -} - -TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml) { - static constexpr const char* system_manifest_xml_path = "/system/manifest.xml"; - if (access(system_manifest_xml_path, R_OK) == -1) { - GTEST_LOG_(INFO) << "Test skipped on devices w/o /system/manifest.xml."; - return; - } - std::string system_manifest_xml_content; - ASSERT_TRUE( - android::base::ReadFileToString(system_manifest_xml_path, &system_manifest_xml_content)); - TemporaryFile compatibility_zip_file; - BuildZipArchive({ { "system_manifest.xml", system_manifest_xml_content } }, - compatibility_zip_file.release(), kCompressDeflated); - - TemporaryFile temp_file; - std::string compatibility_zip_content; - ASSERT_TRUE( - android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content)); - BuildZipArchive({ { "compatibility.zip", compatibility_zip_content } }, temp_file.release(), - kCompressStored); - - ZipArchiveHandle zip; - ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - std::vector<std::string> compatibility_info; - compatibility_info.push_back(system_manifest_xml_content); - std::string err; - bool result = - android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err) == 0; - // Make sure the result is consistent with libvintf library. - ASSERT_EQ(result, verify_package_compatibility(zip)); - CloseArchive(zip); -} - -TEST(InstallTest, SetUpNonAbUpdateCommands) { - TemporaryFile temp_file; - static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary"; - BuildZipArchive({ { UPDATE_BINARY_NAME, "" } }, temp_file.release(), kCompressStored); - - ZipArchiveHandle zip; - ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - int status_fd = 10; - std::string package = "/path/to/update.zip"; - TemporaryDir td; - std::string binary_path = std::string(td.path) + "/update_binary"; - Paths::Get().set_temporary_update_binary(binary_path); - std::vector<std::string> cmd; - ASSERT_EQ(0, SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd)); - ASSERT_EQ(4U, cmd.size()); - ASSERT_EQ(binary_path, cmd[0]); - ASSERT_EQ("3", cmd[1]); // RECOVERY_API_VERSION - ASSERT_EQ(std::to_string(status_fd), cmd[2]); - ASSERT_EQ(package, cmd[3]); - struct stat sb; - ASSERT_EQ(0, stat(binary_path.c_str(), &sb)); - ASSERT_EQ(static_cast<mode_t>(0755), sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); - - // With non-zero retry count. update_binary will be removed automatically. - cmd.clear(); - ASSERT_EQ(0, SetUpNonAbUpdateCommands(package, zip, 2, status_fd, &cmd)); - ASSERT_EQ(5U, cmd.size()); - ASSERT_EQ(binary_path, cmd[0]); - ASSERT_EQ("3", cmd[1]); // RECOVERY_API_VERSION - ASSERT_EQ(std::to_string(status_fd), cmd[2]); - ASSERT_EQ(package, cmd[3]); - ASSERT_EQ("retry", cmd[4]); - sb = {}; - ASSERT_EQ(0, stat(binary_path.c_str(), &sb)); - ASSERT_EQ(static_cast<mode_t>(0755), sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); - - CloseArchive(zip); -} - -TEST(InstallTest, SetUpNonAbUpdateCommands_MissingUpdateBinary) { - TemporaryFile temp_file; - // The archive must have something to be opened correctly. - BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored); - - // Missing update binary. - ZipArchiveHandle zip; - ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - int status_fd = 10; - std::string package = "/path/to/update.zip"; - TemporaryDir td; - Paths::Get().set_temporary_update_binary(std::string(td.path) + "/update_binary"); - std::vector<std::string> cmd; - ASSERT_EQ(INSTALL_CORRUPT, SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd)); - CloseArchive(zip); -} - -static void VerifyAbUpdateCommands(const std::string& serialno, bool success = true) { - TemporaryFile temp_file; - - const std::string properties = "some_properties"; - std::string device = android::base::GetProperty("ro.product.device", ""); - ASSERT_NE("", device); - std::string timestamp = android::base::GetProperty("ro.build.date.utc", ""); - ASSERT_NE("", timestamp); - - std::vector<std::string> meta{ "ota-type=AB", "pre-device=" + device, - "post-timestamp=" + timestamp }; - if (!serialno.empty()) { - meta.push_back("serialno=" + serialno); - } - std::string metadata_string = android::base::Join(meta, "\n"); - - BuildZipArchive({ { "payload.bin", "" }, - { "payload_properties.txt", properties }, - { "META-INF/com/android/metadata", metadata_string } }, - temp_file.release(), kCompressStored); - - ZipArchiveHandle zip; - ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - ZipString payload_name("payload.bin"); - ZipEntry payload_entry; - ASSERT_EQ(0, FindEntry(zip, payload_name, &payload_entry)); - - std::map<std::string, std::string> metadata; - ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); - if (success) { - ASSERT_EQ(0, CheckPackageMetadata(metadata, OtaType::AB)); - - int status_fd = 10; - std::string package = "/path/to/update.zip"; - std::vector<std::string> cmd; - ASSERT_EQ(0, SetUpAbUpdateCommands(package, zip, status_fd, &cmd)); - ASSERT_EQ(5U, cmd.size()); - ASSERT_EQ("/system/bin/update_engine_sideload", cmd[0]); - ASSERT_EQ("--payload=file://" + package, cmd[1]); - ASSERT_EQ("--offset=" + std::to_string(payload_entry.offset), cmd[2]); - ASSERT_EQ("--headers=" + properties, cmd[3]); - ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]); - } else { - ASSERT_EQ(INSTALL_ERROR, CheckPackageMetadata(metadata, OtaType::AB)); - } - CloseArchive(zip); -} - -TEST(InstallTest, SetUpAbUpdateCommands) { - // Empty serialno will pass the verification. - VerifyAbUpdateCommands({}); -} - -TEST(InstallTest, SetUpAbUpdateCommands_MissingPayloadPropertiesTxt) { - TemporaryFile temp_file; - - std::string device = android::base::GetProperty("ro.product.device", ""); - ASSERT_NE("", device); - std::string timestamp = android::base::GetProperty("ro.build.date.utc", ""); - ASSERT_NE("", timestamp); - std::string metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=AB", "pre-device=" + device, "post-timestamp=" + timestamp, - }, - "\n"); - - BuildZipArchive( - { - { "payload.bin", "" }, - { "META-INF/com/android/metadata", metadata }, - }, - temp_file.release(), kCompressStored); - - ZipArchiveHandle zip; - ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - int status_fd = 10; - std::string package = "/path/to/update.zip"; - std::vector<std::string> cmd; - ASSERT_EQ(INSTALL_CORRUPT, SetUpAbUpdateCommands(package, zip, status_fd, &cmd)); - CloseArchive(zip); -} - -TEST(InstallTest, SetUpAbUpdateCommands_MultipleSerialnos) { - std::string serialno = android::base::GetProperty("ro.serialno", ""); - ASSERT_NE("", serialno); - - // Single matching serialno will pass the verification. - VerifyAbUpdateCommands(serialno); - - static constexpr char alphabet[] = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - auto generator = []() { return alphabet[rand() % (sizeof(alphabet) - 1)]; }; - - // Generate 900 random serial numbers. - std::string random_serialno; - for (size_t i = 0; i < 900; i++) { - generate_n(back_inserter(random_serialno), serialno.size(), generator); - random_serialno.append("|"); - } - // Random serialnos should fail the verification. - VerifyAbUpdateCommands(random_serialno, false); - - std::string long_serialno = random_serialno + serialno + "|"; - for (size_t i = 0; i < 99; i++) { - generate_n(back_inserter(long_serialno), serialno.size(), generator); - long_serialno.append("|"); - } - // String with the matching serialno should pass the verification. - VerifyAbUpdateCommands(long_serialno); -} - -static void test_check_package_metadata(const std::string& metadata_string, OtaType ota_type, - int exptected_result) { - TemporaryFile temp_file; - BuildZipArchive( - { - { "META-INF/com/android/metadata", metadata_string }, - }, - temp_file.release(), kCompressStored); - - ZipArchiveHandle zip; - ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - - std::map<std::string, std::string> metadata; - ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); - ASSERT_EQ(exptected_result, CheckPackageMetadata(metadata, ota_type)); - CloseArchive(zip); -} - -TEST(InstallTest, CheckPackageMetadata_ota_type) { - std::string device = android::base::GetProperty("ro.product.device", ""); - ASSERT_NE("", device); - - // ota-type must be present - std::string metadata = android::base::Join( - std::vector<std::string>{ - "pre-device=" + device, - "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), - }, - "\n"); - test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); - - // Checks if ota-type matches - metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=AB", - "pre-device=" + device, - "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), - }, - "\n"); - test_check_package_metadata(metadata, OtaType::AB, 0); - - test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); -} - -TEST(InstallTest, CheckPackageMetadata_device_type) { - // device type can not be empty - std::string metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=BRICK", - }, - "\n"); - test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); - - // device type mismatches - metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=BRICK", - "pre-device=dummy_device_type", - }, - "\n"); - test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); -} - -TEST(InstallTest, CheckPackageMetadata_serial_number_smoke) { - std::string device = android::base::GetProperty("ro.product.device", ""); - ASSERT_NE("", device); - - // Serial number doesn't need to exist - std::string metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=BRICK", - "pre-device=" + device, - }, - "\n"); - test_check_package_metadata(metadata, OtaType::BRICK, 0); - - // Serial number mismatches - metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=BRICK", - "pre-device=" + device, - "serialno=dummy_serial", - }, - "\n"); - test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); - - std::string serialno = android::base::GetProperty("ro.serialno", ""); - ASSERT_NE("", serialno); - metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=BRICK", - "pre-device=" + device, - "serialno=" + serialno, - }, - "\n"); - test_check_package_metadata(metadata, OtaType::BRICK, 0); -} - -TEST(InstallTest, CheckPackageMetadata_multiple_serial_number) { - std::string device = android::base::GetProperty("ro.product.device", ""); - ASSERT_NE("", device); - - std::string serialno = android::base::GetProperty("ro.serialno", ""); - ASSERT_NE("", serialno); - - std::vector<std::string> serial_numbers; - // Creates a dummy serial number string. - for (char c = 'a'; c <= 'z'; c++) { - serial_numbers.emplace_back(serialno.size(), c); - } - - // No matched serialno found. - std::string metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=BRICK", - "pre-device=" + device, - "serialno=" + android::base::Join(serial_numbers, '|'), - }, - "\n"); - test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); - - serial_numbers.emplace_back(serialno); - std::shuffle(serial_numbers.begin(), serial_numbers.end(), std::default_random_engine()); - metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=BRICK", - "pre-device=" + device, - "serialno=" + android::base::Join(serial_numbers, '|'), - }, - "\n"); - test_check_package_metadata(metadata, OtaType::BRICK, 0); -} - -TEST(InstallTest, CheckPackageMetadata_ab_build_version) { - std::string device = android::base::GetProperty("ro.product.device", ""); - ASSERT_NE("", device); - - std::string build_version = android::base::GetProperty("ro.build.version.incremental", ""); - ASSERT_NE("", build_version); - - std::string metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=AB", - "pre-device=" + device, - "pre-build-incremental=" + build_version, - "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), - }, - "\n"); - test_check_package_metadata(metadata, OtaType::AB, 0); - - metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=AB", - "pre-device=" + device, - "pre-build-incremental=dummy_build", - "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), - }, - "\n"); - test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); -} - -TEST(InstallTest, CheckPackageMetadata_ab_fingerprint) { - std::string device = android::base::GetProperty("ro.product.device", ""); - ASSERT_NE("", device); - - std::string finger_print = android::base::GetProperty("ro.build.fingerprint", ""); - ASSERT_NE("", finger_print); - - std::string metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=AB", - "pre-device=" + device, - "pre-build=" + finger_print, - "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), - }, - "\n"); - test_check_package_metadata(metadata, OtaType::AB, 0); - - metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=AB", - "pre-device=" + device, - "pre-build=dummy_build_fingerprint", - "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), - }, - "\n"); - test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); -} - -TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) { - std::string device = android::base::GetProperty("ro.product.device", ""); - ASSERT_NE("", device); - - // post timestamp is required for upgrade. - std::string metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=AB", - "pre-device=" + device, - }, - "\n"); - test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); - - // post timestamp should be larger than the timestamp on device. - metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=AB", - "pre-device=" + device, - "post-timestamp=0", - }, - "\n"); - test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); - - // fingerprint is required for downgrade - metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=AB", - "pre-device=" + device, - "post-timestamp=0", - "ota-downgrade=yes", - }, - "\n"); - test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); - - std::string finger_print = android::base::GetProperty("ro.build.fingerprint", ""); - ASSERT_NE("", finger_print); - - metadata = android::base::Join( - std::vector<std::string>{ - "ota-type=AB", - "pre-device=" + device, - "post-timestamp=0", - "pre-build=" + finger_print, - "ota-downgrade=yes", - }, - "\n"); - test_check_package_metadata(metadata, OtaType::AB, 0); -} diff --git a/tests/component/resources_test.cpp b/tests/component/resources_test.cpp deleted file mode 100644 index d7fdb8fa0..000000000 --- a/tests/component/resources_test.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <dirent.h> -#include <stdio.h> -#include <stdlib.h> - -#include <memory> -#include <string> -#include <vector> - -#include <android-base/file.h> -#include <android-base/strings.h> -#include <gtest/gtest.h> -#include <png.h> - -#include "minui/minui.h" -#include "private/resources.h" - -static const std::string kLocale = "zu"; - -static const std::vector<std::string> kResourceImagesDirs{ - "res-mdpi/images/", "res-hdpi/images/", "res-xhdpi/images/", - "res-xxhdpi/images/", "res-xxxhdpi/images/", -}; - -static int png_filter(const dirent* de) { - if (de->d_type != DT_REG || !android::base::EndsWith(de->d_name, "_text.png")) { - return 0; - } - return 1; -} - -// Finds out all the PNG files to test, which stay under the same dir with the executabl.. -static std::vector<std::string> add_files() { - std::vector<std::string> files; - for (const std::string& images_dir : kResourceImagesDirs) { - static std::string exec_dir = android::base::GetExecutableDirectory(); - std::string dir_path = exec_dir + "/" + images_dir; - dirent** namelist; - int n = scandir(dir_path.c_str(), &namelist, png_filter, alphasort); - if (n == -1) { - printf("Failed to scandir %s: %s\n", dir_path.c_str(), strerror(errno)); - continue; - } - if (n == 0) { - printf("No file is added for test in %s\n", dir_path.c_str()); - } - - while (n--) { - std::string file_path = dir_path + namelist[n]->d_name; - files.push_back(file_path); - free(namelist[n]); - } - free(namelist); - } - return files; -} - -class ResourcesTest : public testing::TestWithParam<std::string> { - public: - static std::vector<std::string> png_list; - - protected: - void SetUp() override { - png_ = std::make_unique<PngHandler>(GetParam()); - ASSERT_TRUE(png_); - - ASSERT_EQ(PNG_COLOR_TYPE_GRAY, png_->color_type()) << "Recovery expects grayscale PNG file."; - ASSERT_LT(static_cast<png_uint_32>(5), png_->width()); - ASSERT_LT(static_cast<png_uint_32>(0), png_->height()); - ASSERT_EQ(1, png_->channels()) << "Recovery background text images expects 1-channel PNG file."; - } - - std::unique_ptr<PngHandler> png_{ nullptr }; -}; - -// Parses a png file and tests if it's qualified for the background text image under recovery. -TEST_P(ResourcesTest, ValidateLocale) { - std::vector<unsigned char> row(png_->width()); - for (png_uint_32 y = 0; y < png_->height(); ++y) { - png_read_row(png_->png_ptr(), row.data(), nullptr); - int w = (row[1] << 8) | row[0]; - int h = (row[3] << 8) | row[2]; - int len = row[4]; - EXPECT_LT(0, w); - EXPECT_LT(0, h); - EXPECT_LT(0, len) << "Locale string should be non-empty."; - EXPECT_NE(0, row[5]) << "Locale string is missing."; - - ASSERT_GE(png_->height(), y + 1 + h) << "Locale: " << kLocale << " is not found in the file."; - char* loc = reinterpret_cast<char*>(&row[5]); - if (matches_locale(loc, kLocale.c_str())) { - EXPECT_TRUE(android::base::StartsWith(loc, kLocale)); - break; - } - for (int i = 0; i < h; ++i, ++y) { - png_read_row(png_->png_ptr(), row.data(), nullptr); - } - } -} - -std::vector<std::string> ResourcesTest::png_list = add_files(); - -INSTANTIATE_TEST_CASE_P(BackgroundTextValidation, ResourcesTest, - ::testing::ValuesIn(ResourcesTest::png_list.cbegin(), - ResourcesTest::png_list.cend())); diff --git a/tests/component/sideload_test.cpp b/tests/component/sideload_test.cpp deleted file mode 100644 index 6add99f41..000000000 --- a/tests/component/sideload_test.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <unistd.h> - -#include <memory> -#include <string> -#include <vector> - -#include <android-base/file.h> -#include <android-base/strings.h> -#include <gtest/gtest.h> - -#include "fuse_provider.h" -#include "fuse_sideload.h" - -TEST(SideloadTest, fuse_device) { - ASSERT_EQ(0, access("/dev/fuse", R_OK | W_OK)); -} - -class FuseTestDataProvider : public FuseDataProvider { - public: - FuseTestDataProvider(uint64_t file_size, uint32_t block_size) - : FuseDataProvider(file_size, block_size) {} - - private: - bool ReadBlockAlignedData(uint8_t*, uint32_t, uint32_t) const override { - return true; - } -}; - -TEST(SideloadTest, run_fuse_sideload_wrong_parameters) { - auto provider_small_block = std::make_unique<FuseTestDataProvider>(4096, 4095); - ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_small_block))); - - auto provider_large_block = std::make_unique<FuseTestDataProvider>(4096, (1 << 22) + 1); - ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_large_block))); - - auto provider_too_many_blocks = - std::make_unique<FuseTestDataProvider>(((1 << 18) + 1) * 4096, 4096); - ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_too_many_blocks))); -} - -TEST(SideloadTest, run_fuse_sideload) { - const std::vector<std::string> blocks = { - std::string(2048, 'a') + std::string(2048, 'b'), - std::string(2048, 'c') + std::string(2048, 'd'), - std::string(2048, 'e') + std::string(2048, 'f'), - std::string(2048, 'g') + std::string(2048, 'h'), - }; - const std::string content = android::base::Join(blocks, ""); - ASSERT_EQ(16384U, content.size()); - - TemporaryFile temp_file; - ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path)); - - auto provider = std::make_unique<FuseFileDataProvider>(temp_file.path, 4096); - ASSERT_TRUE(provider->Valid()); - TemporaryDir mount_point; - pid_t pid = fork(); - if (pid == 0) { - ASSERT_EQ(0, run_fuse_sideload(std::move(provider), mount_point.path)); - _exit(EXIT_SUCCESS); - } - - std::string package = std::string(mount_point.path) + "/" + FUSE_SIDELOAD_HOST_FILENAME; - int status; - static constexpr int kSideloadInstallTimeout = 10; - for (int i = 0; i < kSideloadInstallTimeout; ++i) { - ASSERT_NE(-1, waitpid(pid, &status, WNOHANG)); - - struct stat sb; - if (stat(package.c_str(), &sb) == 0) { - break; - } - - if (errno == ENOENT && i < kSideloadInstallTimeout - 1) { - sleep(1); - continue; - } - FAIL() << "Timed out waiting for the fuse-provided package."; - } - - std::string content_via_fuse; - ASSERT_TRUE(android::base::ReadFileToString(package, &content_via_fuse)); - ASSERT_EQ(content, content_via_fuse); - - std::string exit_flag = std::string(mount_point.path) + "/" + FUSE_SIDELOAD_HOST_EXIT_FLAG; - struct stat sb; - ASSERT_EQ(0, stat(exit_flag.c_str(), &sb)); - - waitpid(pid, &status, 0); - ASSERT_EQ(0, WEXITSTATUS(status)); - ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); -} diff --git a/tests/component/uncrypt_test.cpp b/tests/component/uncrypt_test.cpp deleted file mode 100644 index e97d589a6..000000000 --- a/tests/component/uncrypt_test.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <arpa/inet.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> -#include <unistd.h> - -#include <algorithm> -#include <string> - -#include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/properties.h> -#include <android-base/unique_fd.h> -#include <bootloader_message/bootloader_message.h> -#include <gtest/gtest.h> - -using namespace std::string_literals; - -static const std::string UNCRYPT_SOCKET = "/dev/socket/uncrypt"; -static const std::string INIT_SVC_SETUP_BCB = "init.svc.setup-bcb"; -static const std::string INIT_SVC_CLEAR_BCB = "init.svc.clear-bcb"; -static const std::string INIT_SVC_UNCRYPT = "init.svc.uncrypt"; -static constexpr int SOCKET_CONNECTION_MAX_RETRY = 30; - -static void StopService() { - ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb")); - ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb")); - ASSERT_TRUE(android::base::SetProperty("ctl.stop", "uncrypt")); - - bool success = false; - for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { - std::string setup_bcb = android::base::GetProperty(INIT_SVC_SETUP_BCB, ""); - std::string clear_bcb = android::base::GetProperty(INIT_SVC_CLEAR_BCB, ""); - std::string uncrypt = android::base::GetProperty(INIT_SVC_UNCRYPT, ""); - GTEST_LOG_(INFO) << "setup-bcb: [" << setup_bcb << "] clear-bcb: [" << clear_bcb - << "] uncrypt: [" << uncrypt << "]"; - if (setup_bcb != "running" && clear_bcb != "running" && uncrypt != "running") { - success = true; - break; - } - sleep(1); - } - - ASSERT_TRUE(success) << "uncrypt service is not available."; -} - -class UncryptTest : public ::testing::Test { - protected: - UncryptTest() : has_misc(true) {} - - void SetUp() override { - std::string err; - has_misc = !get_bootloader_message_blk_device(&err).empty(); - } - - void TearDown() override { - // Clear the BCB. - if (has_misc) { - std::string err; - ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; - } - } - - void SetupOrClearBcb(bool isSetup, const std::string& message, - const std::string& message_in_bcb) const { - // Restart the setup-bcb service. - StopService(); - ASSERT_TRUE(android::base::SetProperty("ctl.start", isSetup ? "setup-bcb" : "clear-bcb")); - - // Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected"). - sleep(1); - - sockaddr_un un = {}; - un.sun_family = AF_UNIX; - strlcpy(un.sun_path, UNCRYPT_SOCKET.c_str(), sizeof(un.sun_path)); - - int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - ASSERT_NE(-1, sockfd); - - // Connect to the uncrypt socket. - bool success = false; - for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { - if (connect(sockfd, reinterpret_cast<sockaddr*>(&un), sizeof(sockaddr_un)) != 0) { - success = true; - break; - } - sleep(1); - } - ASSERT_TRUE(success); - - if (isSetup) { - // Send out the BCB message. - int length = static_cast<int>(message.size()); - int length_out = htonl(length); - ASSERT_TRUE(android::base::WriteFully(sockfd, &length_out, sizeof(int))) - << "Failed to write length: " << strerror(errno); - ASSERT_TRUE(android::base::WriteFully(sockfd, message.data(), length)) - << "Failed to write message: " << strerror(errno); - } - - // Check the status code from uncrypt. - int status; - ASSERT_TRUE(android::base::ReadFully(sockfd, &status, sizeof(int))); - ASSERT_EQ(100U, ntohl(status)); - - // Ack having received the status code. - int code = 0; - ASSERT_TRUE(android::base::WriteFully(sockfd, &code, sizeof(int))); - - ASSERT_EQ(0, close(sockfd)); - - ASSERT_TRUE(android::base::SetProperty("ctl.stop", isSetup ? "setup-bcb" : "clear-bcb")); - - // Verify the message by reading from BCB directly. - bootloader_message boot; - std::string err; - ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; - - if (isSetup) { - ASSERT_EQ("boot-recovery", std::string(boot.command)); - ASSERT_EQ(message_in_bcb, std::string(boot.recovery)); - - // The rest of the boot.recovery message should be zero'd out. - ASSERT_LE(message_in_bcb.size(), sizeof(boot.recovery)); - size_t left = sizeof(boot.recovery) - message_in_bcb.size(); - ASSERT_EQ(std::string(left, '\0'), std::string(&boot.recovery[message_in_bcb.size()], left)); - - // Clear the BCB. - ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; - } else { - // All the bytes should be cleared. - ASSERT_EQ(std::string(sizeof(boot), '\0'), - std::string(reinterpret_cast<const char*>(&boot), sizeof(boot))); - } - } - - void VerifyBootloaderMessage(const std::string& expected) { - std::string err; - bootloader_message boot; - ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; - - // Check that we have all the expected bytes. - ASSERT_EQ(expected, std::string(reinterpret_cast<const char*>(&boot), sizeof(boot))); - } - - bool has_misc; -}; - -TEST_F(UncryptTest, setup_bcb) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } - - std::string random_data; - random_data.reserve(sizeof(bootloader_message)); - generate_n(back_inserter(random_data), sizeof(bootloader_message), []() { return rand() % 128; }); - - bootloader_message boot; - memcpy(&boot, random_data.c_str(), random_data.size()); - - std::string err; - ASSERT_TRUE(write_bootloader_message(boot, &err)) << "Failed to write BCB: " << err; - VerifyBootloaderMessage(random_data); - - ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; - VerifyBootloaderMessage(std::string(sizeof(bootloader_message), '\0')); - - std::string message = "--update_message=abc value"; - std::string message_in_bcb = "recovery\n--update_message=abc value\n"; - SetupOrClearBcb(true, message, message_in_bcb); - - SetupOrClearBcb(false, "", ""); - - TemporaryFile wipe_package; - ASSERT_TRUE(android::base::WriteStringToFile(std::string(345, 'a'), wipe_package.path)); - - // It's expected to store a wipe package in /misc, with the package size passed to recovery. - message = "--wipe_ab\n--wipe_package="s + wipe_package.path + "\n--reason=wipePackage"s; - message_in_bcb = "recovery\n--wipe_ab\n--wipe_package_size=345\n--reason=wipePackage\n"; - SetupOrClearBcb(true, message, message_in_bcb); -} diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp deleted file mode 100644 index e27e58c22..000000000 --- a/tests/component/update_verifier_test.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <update_verifier/update_verifier.h> - -#include <functional> -#include <string> -#include <unordered_map> -#include <vector> - -#include <android-base/file.h> -#include <android-base/properties.h> -#include <android-base/strings.h> -#include <google/protobuf/repeated_field.h> -#include <gtest/gtest.h> - -#include "care_map.pb.h" - -using namespace std::string_literals; - -class UpdateVerifierTest : public ::testing::Test { - protected: - void SetUp() override { - std::string verity_mode = android::base::GetProperty("ro.boot.veritymode", ""); - verity_supported = android::base::EqualsIgnoreCase(verity_mode, "enforcing"); - - care_map_prefix_ = care_map_dir_.path + "/care_map"s; - care_map_pb_ = care_map_dir_.path + "/care_map.pb"s; - care_map_txt_ = care_map_dir_.path + "/care_map.txt"s; - // Overrides the the care_map_prefix. - verifier_.set_care_map_prefix(care_map_prefix_); - - property_id_ = "ro.build.fingerprint"; - fingerprint_ = android::base::GetProperty(property_id_, ""); - // Overrides the property_reader if we cannot read the given property on the device. - if (fingerprint_.empty()) { - fingerprint_ = "mock_fingerprint"; - verifier_.set_property_reader([](const std::string& /* id */) { return "mock_fingerprint"; }); - } - } - - void TearDown() override { - unlink(care_map_pb_.c_str()); - unlink(care_map_txt_.c_str()); - } - - // Returns a serialized string of the proto3 message according to the given partition info. - std::string ConstructProto( - std::vector<std::unordered_map<std::string, std::string>>& partitions) { - recovery_update_verifier::CareMap result; - for (const auto& partition : partitions) { - recovery_update_verifier::CareMap::PartitionInfo info; - if (partition.find("name") != partition.end()) { - info.set_name(partition.at("name")); - } - if (partition.find("ranges") != partition.end()) { - info.set_ranges(partition.at("ranges")); - } - if (partition.find("id") != partition.end()) { - info.set_id(partition.at("id")); - } - if (partition.find("fingerprint") != partition.end()) { - info.set_fingerprint(partition.at("fingerprint")); - } - - *result.add_partitions() = info; - } - - return result.SerializeAsString(); - } - - bool verity_supported; - UpdateVerifier verifier_; - - TemporaryDir care_map_dir_; - std::string care_map_prefix_; - std::string care_map_pb_; - std::string care_map_txt_; - - std::string property_id_; - std::string fingerprint_; -}; - -TEST_F(UpdateVerifierTest, verify_image_no_care_map) { - ASSERT_FALSE(verifier_.ParseCareMap()); -} - -TEST_F(UpdateVerifierTest, verify_image_text_format) { - // This test relies on dm-verity support. - if (!verity_supported) { - GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; - return; - } - - std::string content = "system\n2,0,1"; - ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_txt_)); - // CareMap in text format is no longer supported. - ASSERT_FALSE(verifier_.ParseCareMap()); -} - -TEST_F(UpdateVerifierTest, verify_image_empty_care_map) { - ASSERT_FALSE(verifier_.ParseCareMap()); -} - -TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_smoke) { - // This test relies on dm-verity support. - if (!verity_supported) { - GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; - return; - } - - std::vector<std::unordered_map<std::string, std::string>> partitions = { - { - { "name", "system" }, - { "ranges", "2,0,1" }, - { "id", property_id_ }, - { "fingerprint", fingerprint_ }, - }, - }; - - std::string proto = ConstructProto(partitions); - ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_pb_)); - ASSERT_TRUE(verifier_.ParseCareMap()); - ASSERT_TRUE(verifier_.VerifyPartitions()); -} - -TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_missing_name) { - // This test relies on dm-verity support. - if (!verity_supported) { - GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; - return; - } - - std::vector<std::unordered_map<std::string, std::string>> partitions = { - { - { "ranges", "2,0,1" }, - { "id", property_id_ }, - { "fingerprint", fingerprint_ }, - }, - }; - - std::string proto = ConstructProto(partitions); - ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_pb_)); - ASSERT_FALSE(verifier_.ParseCareMap()); -} - -TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_bad_ranges) { - // This test relies on dm-verity support. - if (!verity_supported) { - GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; - return; - } - - std::vector<std::unordered_map<std::string, std::string>> partitions = { - { - { "name", "system" }, - { "ranges", "3,0,1" }, - { "id", property_id_ }, - { "fingerprint", fingerprint_ }, - }, - }; - - std::string proto = ConstructProto(partitions); - ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_pb_)); - ASSERT_FALSE(verifier_.ParseCareMap()); -} - -TEST_F(UpdateVerifierTest, verify_image_protobuf_empty_fingerprint) { - // This test relies on dm-verity support. - if (!verity_supported) { - GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; - return; - } - - std::vector<std::unordered_map<std::string, std::string>> partitions = { - { - { "name", "system" }, - { "ranges", "2,0,1" }, - }, - }; - - std::string proto = ConstructProto(partitions); - ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_pb_)); - ASSERT_FALSE(verifier_.ParseCareMap()); -} - -TEST_F(UpdateVerifierTest, verify_image_protobuf_fingerprint_mismatch) { - // This test relies on dm-verity support. - if (!verity_supported) { - GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; - return; - } - - std::vector<std::unordered_map<std::string, std::string>> partitions = { - { - { "name", "system" }, - { "ranges", "2,0,1" }, - { "id", property_id_ }, - { "fingerprint", "unsupported_fingerprint" }, - }, - }; - - std::string proto = ConstructProto(partitions); - ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_pb_)); - ASSERT_FALSE(verifier_.ParseCareMap()); -} diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp deleted file mode 100644 index a0a7b66ab..000000000 --- a/tests/component/updater_test.cpp +++ /dev/null @@ -1,1225 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include <algorithm> -#include <memory> -#include <string> -#include <string_view> -#include <unordered_map> -#include <vector> - -#include <android-base/file.h> -#include <android-base/logging.h> -#include <android-base/parseint.h> -#include <android-base/properties.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> -#include <bootloader_message/bootloader_message.h> -#include <brotli/encode.h> -#include <bsdiff/bsdiff.h> -#include <gtest/gtest.h> -#include <verity/hash_tree_builder.h> -#include <ziparchive/zip_archive.h> -#include <ziparchive/zip_writer.h> - -#include "applypatch/applypatch.h" -#include "common/test_constants.h" -#include "edify/expr.h" -#include "otautil/error_code.h" -#include "otautil/paths.h" -#include "otautil/print_sha1.h" -#include "otautil/sysutil.h" -#include "private/commands.h" -#include "updater/blockimg.h" -#include "updater/install.h" -#include "updater/updater.h" - -using namespace std::string_literals; - -using PackageEntries = std::unordered_map<std::string, std::string>; - -struct selabel_handle* sehandle = nullptr; - -static void expect(const char* expected, const std::string& expr_str, CauseCode cause_code, - UpdaterInfo* info = nullptr) { - std::unique_ptr<Expr> e; - int error_count = 0; - ASSERT_EQ(0, ParseString(expr_str, &e, &error_count)); - ASSERT_EQ(0, error_count); - - State state(expr_str, info); - - std::string result; - bool status = Evaluate(&state, e, &result); - - if (expected == nullptr) { - ASSERT_FALSE(status); - } else { - ASSERT_TRUE(status) << "Evaluate() finished with error message: " << state.errmsg; - ASSERT_STREQ(expected, result.c_str()); - } - - // Error code is set in updater/updater.cpp only, by parsing State.errmsg. - ASSERT_EQ(kNoError, state.error_code); - - // Cause code should always be available. - ASSERT_EQ(cause_code, state.cause_code); -} - -static void BuildUpdatePackage(const PackageEntries& entries, int fd) { - FILE* zip_file_ptr = fdopen(fd, "wb"); - ZipWriter zip_writer(zip_file_ptr); - - for (const auto& entry : entries) { - // All the entries are written as STORED. - ASSERT_EQ(0, zip_writer.StartEntry(entry.first.c_str(), 0)); - if (!entry.second.empty()) { - ASSERT_EQ(0, zip_writer.WriteBytes(entry.second.data(), entry.second.size())); - } - ASSERT_EQ(0, zip_writer.FinishEntry()); - } - - ASSERT_EQ(0, zip_writer.Finish()); - ASSERT_EQ(0, fclose(zip_file_ptr)); -} - -static void RunBlockImageUpdate(bool is_verify, const PackageEntries& entries, - const std::string& image_file, const std::string& result, - CauseCode cause_code = kNoCause) { - CHECK(entries.find("transfer_list") != entries.end()); - - // Build the update package. - TemporaryFile zip_file; - BuildUpdatePackage(entries, zip_file.release()); - - MemMapping map; - ASSERT_TRUE(map.MapFile(zip_file.path)); - ZipArchiveHandle handle; - ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); - - // Set up the handler, command_pipe, patch offset & length. - UpdaterInfo updater_info; - updater_info.package_zip = handle; - TemporaryFile temp_pipe; - updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); - updater_info.package_zip_addr = map.addr; - updater_info.package_zip_len = map.length; - - std::string new_data = entries.find("new_data.br") != entries.end() ? "new_data.br" : "new_data"; - std::string script = is_verify ? "block_image_verify" : "block_image_update"; - script += R"((")" + image_file + R"(", package_extract_file("transfer_list"), ")" + new_data + - R"(", "patch_data"))"; - expect(result.c_str(), script, cause_code, &updater_info); - - ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); - CloseArchive(handle); -} - -static std::string GetSha1(std::string_view content) { - uint8_t digest[SHA_DIGEST_LENGTH]; - SHA1(reinterpret_cast<const uint8_t*>(content.data()), content.size(), digest); - return print_sha1(digest); -} - -static Value* BlobToString(const char* name, State* state, - const std::vector<std::unique_ptr<Expr>>& argv) { - if (argv.size() != 1) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %zu", name, argv.size()); - } - - std::vector<std::unique_ptr<Value>> args; - if (!ReadValueArgs(state, argv, &args)) { - return nullptr; - } - - if (args[0]->type != Value::Type::BLOB) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects a BLOB argument", name); - } - - args[0]->type = Value::Type::STRING; - return args[0].release(); -} - -class UpdaterTest : public ::testing::Test { - protected: - void SetUp() override { - RegisterBuiltins(); - RegisterInstallFunctions(); - RegisterBlockImageFunctions(); - - RegisterFunction("blob_to_string", BlobToString); - - // Each test is run in a separate process (isolated mode). Shared temporary files won't cause - // conflicts. - Paths::Get().set_cache_temp_source(temp_saved_source_.path); - Paths::Get().set_last_command_file(temp_last_command_.path); - Paths::Get().set_stash_directory_base(temp_stash_base_.path); - - // Enable a special command "abort" to simulate interruption. - Command::abort_allowed_ = true; - - last_command_file_ = temp_last_command_.path; - image_file_ = image_temp_file_.path; - } - - void TearDown() override { - // Clean up the last_command_file if any. - ASSERT_TRUE(android::base::RemoveFileIfExists(last_command_file_)); - - // Clear partition updated marker if any. - std::string updated_marker{ temp_stash_base_.path }; - updated_marker += "/" + GetSha1(image_temp_file_.path) + ".UPDATED"; - ASSERT_TRUE(android::base::RemoveFileIfExists(updated_marker)); - } - - TemporaryFile temp_saved_source_; - TemporaryDir temp_stash_base_; - std::string last_command_file_; - std::string image_file_; - - private: - TemporaryFile temp_last_command_; - TemporaryFile image_temp_file_; -}; - -TEST_F(UpdaterTest, getprop) { - expect(android::base::GetProperty("ro.product.device", "").c_str(), - "getprop(\"ro.product.device\")", - kNoCause); - - expect(android::base::GetProperty("ro.build.fingerprint", "").c_str(), - "getprop(\"ro.build.fingerprint\")", - kNoCause); - - // getprop() accepts only one parameter. - expect(nullptr, "getprop()", kArgsParsingFailure); - expect(nullptr, "getprop(\"arg1\", \"arg2\")", kArgsParsingFailure); -} - -TEST_F(UpdaterTest, patch_partition_check) { - // Zero argument is not valid. - expect(nullptr, "patch_partition_check()", kArgsParsingFailure); - - std::string source_file = from_testdata_base("boot.img"); - std::string source_content; - ASSERT_TRUE(android::base::ReadFileToString(source_file, &source_content)); - size_t source_size = source_content.size(); - std::string source_hash = GetSha1(source_content); - Partition source(source_file, source_size, source_hash); - - std::string target_file = from_testdata_base("recovery.img"); - std::string target_content; - ASSERT_TRUE(android::base::ReadFileToString(target_file, &target_content)); - size_t target_size = target_content.size(); - std::string target_hash = GetSha1(target_content); - Partition target(target_file, target_size, target_hash); - - // One argument is not valid. - expect(nullptr, "patch_partition_check(\"" + source.ToString() + "\")", kArgsParsingFailure); - expect(nullptr, "patch_partition_check(\"" + target.ToString() + "\")", kArgsParsingFailure); - - // Both of the source and target have the desired checksum. - std::string cmd = - "patch_partition_check(\"" + source.ToString() + "\", \"" + target.ToString() + "\")"; - expect("t", cmd, kNoCause); - - // Only source partition has the desired checksum. - Partition bad_target(target_file, target_size - 1, target_hash); - cmd = "patch_partition_check(\"" + source.ToString() + "\", \"" + bad_target.ToString() + "\")"; - expect("t", cmd, kNoCause); - - // Only target partition has the desired checksum. - Partition bad_source(source_file, source_size + 1, source_hash); - cmd = "patch_partition_check(\"" + bad_source.ToString() + "\", \"" + target.ToString() + "\")"; - expect("t", cmd, kNoCause); - - // Neither of the source or target has the desired checksum. - cmd = - "patch_partition_check(\"" + bad_source.ToString() + "\", \"" + bad_target.ToString() + "\")"; - expect("", cmd, kNoCause); -} - -TEST_F(UpdaterTest, file_getprop) { - // file_getprop() expects two arguments. - expect(nullptr, "file_getprop()", kArgsParsingFailure); - expect(nullptr, "file_getprop(\"arg1\")", kArgsParsingFailure); - expect(nullptr, "file_getprop(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure); - - // File doesn't exist. - expect(nullptr, "file_getprop(\"/doesntexist\", \"key1\")", kFreadFailure); - - // Reject too large files (current limit = 65536). - TemporaryFile temp_file1; - std::string buffer(65540, '\0'); - ASSERT_TRUE(android::base::WriteStringToFile(buffer, temp_file1.path)); - - // Read some keys. - TemporaryFile temp_file2; - std::string content("ro.product.name=tardis\n" - "# comment\n\n\n" - "ro.product.model\n" - "ro.product.board = magic \n"); - ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file2.path)); - - std::string script1("file_getprop(\"" + std::string(temp_file2.path) + - "\", \"ro.product.name\")"); - expect("tardis", script1, kNoCause); - - std::string script2("file_getprop(\"" + std::string(temp_file2.path) + - "\", \"ro.product.board\")"); - expect("magic", script2, kNoCause); - - // No match. - std::string script3("file_getprop(\"" + std::string(temp_file2.path) + - "\", \"ro.product.wrong\")"); - expect("", script3, kNoCause); - - std::string script4("file_getprop(\"" + std::string(temp_file2.path) + - "\", \"ro.product.name=\")"); - expect("", script4, kNoCause); - - std::string script5("file_getprop(\"" + std::string(temp_file2.path) + - "\", \"ro.product.nam\")"); - expect("", script5, kNoCause); - - std::string script6("file_getprop(\"" + std::string(temp_file2.path) + - "\", \"ro.product.model\")"); - expect("", script6, kNoCause); -} - -// TODO: Test extracting to block device. -TEST_F(UpdaterTest, package_extract_file) { - // package_extract_file expects 1 or 2 arguments. - expect(nullptr, "package_extract_file()", kArgsParsingFailure); - expect(nullptr, "package_extract_file(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure); - - std::string zip_path = from_testdata_base("ziptest_valid.zip"); - ZipArchiveHandle handle; - ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); - - // Need to set up the ziphandle. - UpdaterInfo updater_info; - updater_info.package_zip = handle; - - // Two-argument version. - TemporaryFile temp_file1; - std::string script("package_extract_file(\"a.txt\", \"" + std::string(temp_file1.path) + "\")"); - expect("t", script, kNoCause, &updater_info); - - // Verify the extracted entry. - std::string data; - ASSERT_TRUE(android::base::ReadFileToString(temp_file1.path, &data)); - ASSERT_EQ(kATxtContents, data); - - // Now extract another entry to the same location, which should overwrite. - script = "package_extract_file(\"b.txt\", \"" + std::string(temp_file1.path) + "\")"; - expect("t", script, kNoCause, &updater_info); - - ASSERT_TRUE(android::base::ReadFileToString(temp_file1.path, &data)); - ASSERT_EQ(kBTxtContents, data); - - // Missing zip entry. The two-argument version doesn't abort. - script = "package_extract_file(\"doesntexist\", \"" + std::string(temp_file1.path) + "\")"; - expect("", script, kNoCause, &updater_info); - - // Extract to /dev/full should fail. - script = "package_extract_file(\"a.txt\", \"/dev/full\")"; - expect("", script, kNoCause, &updater_info); - - // One-argument version. package_extract_file() gives a VAL_BLOB, which needs to be converted to - // VAL_STRING for equality test. - script = "blob_to_string(package_extract_file(\"a.txt\")) == \"" + kATxtContents + "\""; - expect("t", script, kNoCause, &updater_info); - - script = "blob_to_string(package_extract_file(\"b.txt\")) == \"" + kBTxtContents + "\""; - expect("t", script, kNoCause, &updater_info); - - // Missing entry. The one-argument version aborts the evaluation. - script = "package_extract_file(\"doesntexist\")"; - expect(nullptr, script, kPackageExtractFileFailure, &updater_info); - - CloseArchive(handle); -} - -TEST_F(UpdaterTest, read_file) { - // read_file() expects one argument. - expect(nullptr, "read_file()", kArgsParsingFailure); - expect(nullptr, "read_file(\"arg1\", \"arg2\")", kArgsParsingFailure); - - // Write some value to file and read back. - TemporaryFile temp_file; - std::string script("write_value(\"foo\", \""s + temp_file.path + "\");"); - expect("t", script, kNoCause); - - script = "read_file(\""s + temp_file.path + "\") == \"foo\""; - expect("t", script, kNoCause); - - script = "read_file(\""s + temp_file.path + "\") == \"bar\""; - expect("", script, kNoCause); - - // It should fail gracefully when read fails. - script = "read_file(\"/doesntexist\")"; - expect("", script, kNoCause); -} - -TEST_F(UpdaterTest, compute_hash_tree_smoke) { - std::string data; - for (unsigned char i = 0; i < 128; i++) { - data += std::string(4096, i); - } - // Appends an additional block for verity data. - data += std::string(4096, 0); - ASSERT_EQ(129 * 4096, data.size()); - ASSERT_TRUE(android::base::WriteStringToFile(data, image_file_)); - - std::string salt = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7"; - std::string expected_root_hash = - "7e0a8d8747f54384014ab996f5b2dc4eb7ff00c630eede7134c9e3f05c0dd8ca"; - // hash_tree_ranges, source_ranges, hash_algorithm, salt_hex, root_hash - std::vector<std::string> tokens{ "compute_hash_tree", "2,128,129", "2,0,128", "sha256", salt, - expected_root_hash }; - std::string hash_tree_command = android::base::Join(tokens, " "); - - std::vector<std::string> transfer_list{ - "4", "2", "0", "2", hash_tree_command, - }; - - PackageEntries entries{ - { "new_data", "" }, - { "patch_data", "" }, - { "transfer_list", android::base::Join(transfer_list, "\n") }, - }; - - RunBlockImageUpdate(false, entries, image_file_, "t"); - - std::string updated; - ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated)); - ASSERT_EQ(129 * 4096, updated.size()); - ASSERT_EQ(data.substr(0, 128 * 4096), updated.substr(0, 128 * 4096)); - - // Computes the SHA256 of the salt + hash_tree_data and expects the result to match with the - // root_hash. - std::vector<unsigned char> salt_bytes; - ASSERT_TRUE(HashTreeBuilder::ParseBytesArrayFromString(salt, &salt_bytes)); - std::vector<unsigned char> hash_tree = std::move(salt_bytes); - hash_tree.insert(hash_tree.end(), updated.begin() + 128 * 4096, updated.end()); - - std::vector<unsigned char> digest(SHA256_DIGEST_LENGTH); - SHA256(hash_tree.data(), hash_tree.size(), digest.data()); - ASSERT_EQ(expected_root_hash, HashTreeBuilder::BytesArrayToString(digest)); -} - -TEST_F(UpdaterTest, compute_hash_tree_root_mismatch) { - std::string data; - for (size_t i = 0; i < 128; i++) { - data += std::string(4096, i); - } - // Appends an additional block for verity data. - data += std::string(4096, 0); - ASSERT_EQ(129 * 4096, data.size()); - // Corrupts one bit - data[4096] = 'A'; - ASSERT_TRUE(android::base::WriteStringToFile(data, image_file_)); - - std::string salt = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7"; - std::string expected_root_hash = - "7e0a8d8747f54384014ab996f5b2dc4eb7ff00c630eede7134c9e3f05c0dd8ca"; - // hash_tree_ranges, source_ranges, hash_algorithm, salt_hex, root_hash - std::vector<std::string> tokens{ "compute_hash_tree", "2,128,129", "2,0,128", "sha256", salt, - expected_root_hash }; - std::string hash_tree_command = android::base::Join(tokens, " "); - - std::vector<std::string> transfer_list{ - "4", "2", "0", "2", hash_tree_command, - }; - - PackageEntries entries{ - { "new_data", "" }, - { "patch_data", "" }, - { "transfer_list", android::base::Join(transfer_list, "\n") }, - }; - - RunBlockImageUpdate(false, entries, image_file_, "", kHashTreeComputationFailure); -} - -TEST_F(UpdaterTest, write_value) { - // write_value() expects two arguments. - expect(nullptr, "write_value()", kArgsParsingFailure); - expect(nullptr, "write_value(\"arg1\")", kArgsParsingFailure); - expect(nullptr, "write_value(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure); - - // filename cannot be empty. - expect(nullptr, "write_value(\"value\", \"\")", kArgsParsingFailure); - - // Write some value to file. - TemporaryFile temp_file; - std::string value = "magicvalue"; - std::string script("write_value(\"" + value + "\", \"" + std::string(temp_file.path) + "\")"); - expect("t", script, kNoCause); - - // Verify the content. - std::string content; - ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &content)); - ASSERT_EQ(value, content); - - // Allow writing empty string. - script = "write_value(\"\", \"" + std::string(temp_file.path) + "\")"; - expect("t", script, kNoCause); - - // Verify the content. - ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &content)); - ASSERT_EQ("", content); - - // It should fail gracefully when write fails. - script = "write_value(\"value\", \"/proc/0/file1\")"; - expect("", script, kNoCause); -} - -TEST_F(UpdaterTest, get_stage) { - // get_stage() expects one argument. - expect(nullptr, "get_stage()", kArgsParsingFailure); - expect(nullptr, "get_stage(\"arg1\", \"arg2\")", kArgsParsingFailure); - expect(nullptr, "get_stage(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure); - - // Set up a local file as BCB. - TemporaryFile tf; - std::string temp_file(tf.path); - bootloader_message boot; - strlcpy(boot.stage, "2/3", sizeof(boot.stage)); - std::string err; - ASSERT_TRUE(write_bootloader_message_to(boot, temp_file, &err)); - - // Can read the stage value. - std::string script("get_stage(\"" + temp_file + "\")"); - expect("2/3", script, kNoCause); - - // Bad BCB path. - script = "get_stage(\"doesntexist\")"; - expect("", script, kNoCause); -} - -TEST_F(UpdaterTest, set_stage) { - // set_stage() expects two arguments. - expect(nullptr, "set_stage()", kArgsParsingFailure); - expect(nullptr, "set_stage(\"arg1\")", kArgsParsingFailure); - expect(nullptr, "set_stage(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure); - - // Set up a local file as BCB. - TemporaryFile tf; - std::string temp_file(tf.path); - bootloader_message boot; - strlcpy(boot.command, "command", sizeof(boot.command)); - strlcpy(boot.stage, "2/3", sizeof(boot.stage)); - std::string err; - ASSERT_TRUE(write_bootloader_message_to(boot, temp_file, &err)); - - // Write with set_stage(). - std::string script("set_stage(\"" + temp_file + "\", \"1/3\")"); - expect(tf.path, script, kNoCause); - - // Verify. - bootloader_message boot_verify; - ASSERT_TRUE(read_bootloader_message_from(&boot_verify, temp_file, &err)); - - // Stage should be updated, with command part untouched. - ASSERT_STREQ("1/3", boot_verify.stage); - ASSERT_STREQ(boot.command, boot_verify.command); - - // Bad BCB path. - script = "set_stage(\"doesntexist\", \"1/3\")"; - expect("", script, kNoCause); - - script = "set_stage(\"/dev/full\", \"1/3\")"; - expect("", script, kNoCause); -} - -TEST_F(UpdaterTest, set_progress) { - // set_progress() expects one argument. - expect(nullptr, "set_progress()", kArgsParsingFailure); - expect(nullptr, "set_progress(\"arg1\", \"arg2\")", kArgsParsingFailure); - - // Invalid progress argument. - expect(nullptr, "set_progress(\"arg1\")", kArgsParsingFailure); - expect(nullptr, "set_progress(\"3x+5\")", kArgsParsingFailure); - expect(nullptr, "set_progress(\".3.5\")", kArgsParsingFailure); - - TemporaryFile tf; - UpdaterInfo updater_info; - updater_info.cmd_pipe = fdopen(tf.release(), "w"); - expect(".52", "set_progress(\".52\")", kNoCause, &updater_info); - fflush(updater_info.cmd_pipe); - - std::string cmd; - ASSERT_TRUE(android::base::ReadFileToString(tf.path, &cmd)); - ASSERT_EQ(android::base::StringPrintf("set_progress %f\n", .52), cmd); - // recovery-updater protocol expects 2 tokens ("set_progress <frac>"). - ASSERT_EQ(2U, android::base::Split(cmd, " ").size()); - ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); -} - -TEST_F(UpdaterTest, show_progress) { - // show_progress() expects two arguments. - expect(nullptr, "show_progress()", kArgsParsingFailure); - expect(nullptr, "show_progress(\"arg1\")", kArgsParsingFailure); - expect(nullptr, "show_progress(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure); - - // Invalid progress arguments. - expect(nullptr, "show_progress(\"arg1\", \"arg2\")", kArgsParsingFailure); - expect(nullptr, "show_progress(\"3x+5\", \"10\")", kArgsParsingFailure); - expect(nullptr, "show_progress(\".3\", \"5a\")", kArgsParsingFailure); - - TemporaryFile tf; - UpdaterInfo updater_info; - updater_info.cmd_pipe = fdopen(tf.release(), "w"); - expect(".52", "show_progress(\".52\", \"10\")", kNoCause, &updater_info); - fflush(updater_info.cmd_pipe); - - std::string cmd; - ASSERT_TRUE(android::base::ReadFileToString(tf.path, &cmd)); - ASSERT_EQ(android::base::StringPrintf("progress %f %d\n", .52, 10), cmd); - // recovery-updater protocol expects 3 tokens ("progress <frac> <secs>"). - ASSERT_EQ(3U, android::base::Split(cmd, " ").size()); - ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); -} - -TEST_F(UpdaterTest, block_image_update_parsing_error) { - std::vector<std::string> transfer_list{ - // clang-format off - "4", - "2", - "0", - // clang-format on - }; - - PackageEntries entries{ - { "new_data", "" }, - { "patch_data", "" }, - { "transfer_list", android::base::Join(transfer_list, '\n') }, - }; - - RunBlockImageUpdate(false, entries, image_file_, "", kArgsParsingFailure); -} - -// Generates the bsdiff of the given source and target images, and writes the result entries. -// target_blocks specifies the block count to be written into the `bsdiff` command, which may be -// different from the given target size in order to trigger overrun / underrun paths. -static void GetEntriesForBsdiff(std::string_view source, std::string_view target, - size_t target_blocks, PackageEntries* entries) { - // Generate the patch data. - TemporaryFile patch_file; - ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(source.data()), source.size(), - reinterpret_cast<const uint8_t*>(target.data()), target.size(), - patch_file.path, nullptr)); - std::string patch_content; - ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch_content)); - - // Create the transfer list that contains a bsdiff. - std::string src_hash = GetSha1(source); - std::string tgt_hash = GetSha1(target); - size_t source_blocks = source.size() / 4096; - std::vector<std::string> transfer_list{ - // clang-format off - "4", - std::to_string(target_blocks), - "0", - "0", - // bsdiff patch_offset patch_length source_hash target_hash target_range source_block_count - // source_range - android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,%zu %zu 2,0,%zu", patch_content.size(), - src_hash.c_str(), tgt_hash.c_str(), target_blocks, source_blocks, - source_blocks), - // clang-format on - }; - - *entries = { - { "new_data", "" }, - { "patch_data", patch_content }, - { "transfer_list", android::base::Join(transfer_list, '\n') }, - }; -} - -TEST_F(UpdaterTest, block_image_update_patch_data) { - // Both source and target images have 10 blocks. - std::string source = - std::string(4096, 'a') + std::string(4096, 'c') + std::string(4096 * 3, '\0'); - std::string target = - std::string(4096, 'b') + std::string(4096, 'd') + std::string(4096 * 3, '\0'); - ASSERT_TRUE(android::base::WriteStringToFile(source, image_file_)); - - PackageEntries entries; - GetEntriesForBsdiff(std::string_view(source).substr(0, 4096 * 2), - std::string_view(target).substr(0, 4096 * 2), 2, &entries); - RunBlockImageUpdate(false, entries, image_file_, "t"); - - // The update_file should be patched correctly. - std::string updated; - ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated)); - ASSERT_EQ(target, updated); -} - -TEST_F(UpdaterTest, block_image_update_patch_overrun) { - // Both source and target images have 10 blocks. - std::string source = - std::string(4096, 'a') + std::string(4096, 'c') + std::string(4096 * 3, '\0'); - std::string target = - std::string(4096, 'b') + std::string(4096, 'd') + std::string(4096 * 3, '\0'); - ASSERT_TRUE(android::base::WriteStringToFile(source, image_file_)); - - // Provide one less block to trigger the overrun path. - PackageEntries entries; - GetEntriesForBsdiff(std::string_view(source).substr(0, 4096 * 2), - std::string_view(target).substr(0, 4096 * 2), 1, &entries); - - // The update should fail due to overrun. - RunBlockImageUpdate(false, entries, image_file_, "", kPatchApplicationFailure); -} - -TEST_F(UpdaterTest, block_image_update_patch_underrun) { - // Both source and target images have 10 blocks. - std::string source = - std::string(4096, 'a') + std::string(4096, 'c') + std::string(4096 * 3, '\0'); - std::string target = - std::string(4096, 'b') + std::string(4096, 'd') + std::string(4096 * 3, '\0'); - ASSERT_TRUE(android::base::WriteStringToFile(source, image_file_)); - - // Provide one more block to trigger the overrun path. - PackageEntries entries; - GetEntriesForBsdiff(std::string_view(source).substr(0, 4096 * 2), - std::string_view(target).substr(0, 4096 * 2), 3, &entries); - - // The update should fail due to underrun. - RunBlockImageUpdate(false, entries, image_file_, "", kPatchApplicationFailure); -} - -TEST_F(UpdaterTest, block_image_update_fail) { - std::string src_content(4096 * 2, 'e'); - std::string src_hash = GetSha1(src_content); - // Stash and free some blocks, then fail the update intentionally. - std::vector<std::string> transfer_list{ - // clang-format off - "4", - "2", - "0", - "2", - "stash " + src_hash + " 2,0,2", - "free " + src_hash, - "abort", - // clang-format on - }; - - // Add a new data of 10 bytes to test the deadlock. - PackageEntries entries{ - { "new_data", std::string(10, 0) }, - { "patch_data", "" }, - { "transfer_list", android::base::Join(transfer_list, '\n') }, - }; - - ASSERT_TRUE(android::base::WriteStringToFile(src_content, image_file_)); - - RunBlockImageUpdate(false, entries, image_file_, ""); - - // Updater generates the stash name based on the input file name. - std::string name_digest = GetSha1(image_file_); - std::string stash_base = std::string(temp_stash_base_.path) + "/" + name_digest; - ASSERT_EQ(0, access(stash_base.c_str(), F_OK)); - // Expect the stashed blocks to be freed. - ASSERT_EQ(-1, access((stash_base + src_hash).c_str(), F_OK)); - ASSERT_EQ(0, rmdir(stash_base.c_str())); -} - -TEST_F(UpdaterTest, new_data_over_write) { - std::vector<std::string> transfer_list{ - // clang-format off - "4", - "1", - "0", - "0", - "new 2,0,1", - // clang-format on - }; - - // Write 4096 + 100 bytes of new data. - PackageEntries entries{ - { "new_data", std::string(4196, 0) }, - { "patch_data", "" }, - { "transfer_list", android::base::Join(transfer_list, '\n') }, - }; - - RunBlockImageUpdate(false, entries, image_file_, "t"); -} - -TEST_F(UpdaterTest, new_data_short_write) { - std::vector<std::string> transfer_list{ - // clang-format off - "4", - "1", - "0", - "0", - "new 2,0,1", - // clang-format on - }; - - PackageEntries entries{ - { "patch_data", "" }, - { "transfer_list", android::base::Join(transfer_list, '\n') }, - }; - - // Updater should report the failure gracefully rather than stuck in deadlock. - entries["new_data"] = ""; - RunBlockImageUpdate(false, entries, image_file_, ""); - - entries["new_data"] = std::string(10, 'a'); - RunBlockImageUpdate(false, entries, image_file_, ""); - - // Expect to write 1 block of new data successfully. - entries["new_data"] = std::string(4096, 'a'); - RunBlockImageUpdate(false, entries, image_file_, "t"); -} - -TEST_F(UpdaterTest, brotli_new_data) { - auto generator = []() { return rand() % 128; }; - // Generate 100 blocks of random data. - std::string brotli_new_data; - brotli_new_data.reserve(4096 * 100); - generate_n(back_inserter(brotli_new_data), 4096 * 100, generator); - - size_t encoded_size = BrotliEncoderMaxCompressedSize(brotli_new_data.size()); - std::string encoded_data(encoded_size, 0); - ASSERT_TRUE(BrotliEncoderCompress( - BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, brotli_new_data.size(), - reinterpret_cast<const uint8_t*>(brotli_new_data.data()), &encoded_size, - reinterpret_cast<uint8_t*>(const_cast<char*>(encoded_data.data())))); - encoded_data.resize(encoded_size); - - // Write a few small chunks of new data, then a large chunk, and finally a few small chunks. - // This helps us to catch potential short writes. - std::vector<std::string> transfer_list = { - "4", - "100", - "0", - "0", - "new 2,0,1", - "new 2,1,2", - "new 4,2,50,50,97", - "new 2,97,98", - "new 2,98,99", - "new 2,99,100", - }; - - PackageEntries entries{ - { "new_data.br", std::move(encoded_data) }, - { "patch_data", "" }, - { "transfer_list", android::base::Join(transfer_list, '\n') }, - }; - - RunBlockImageUpdate(false, entries, image_file_, "t"); - - std::string updated_content; - ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated_content)); - ASSERT_EQ(brotli_new_data, updated_content); -} - -TEST_F(UpdaterTest, last_command_update) { - std::string block1(4096, '1'); - std::string block2(4096, '2'); - std::string block3(4096, '3'); - std::string block1_hash = GetSha1(block1); - std::string block2_hash = GetSha1(block2); - std::string block3_hash = GetSha1(block3); - - // Compose the transfer list to fail the first update. - std::vector<std::string> transfer_list_fail{ - // clang-format off - "4", - "2", - "0", - "2", - "stash " + block1_hash + " 2,0,1", - "move " + block1_hash + " 2,1,2 1 2,0,1", - "stash " + block3_hash + " 2,2,3", - "abort", - // clang-format on - }; - - // Mimic a resumed update with the same transfer commands. - std::vector<std::string> transfer_list_continue{ - // clang-format off - "4", - "2", - "0", - "2", - "stash " + block1_hash + " 2,0,1", - "move " + block1_hash + " 2,1,2 1 2,0,1", - "stash " + block3_hash + " 2,2,3", - "move " + block1_hash + " 2,2,3 1 2,0,1", - // clang-format on - }; - - ASSERT_TRUE(android::base::WriteStringToFile(block1 + block2 + block3, image_file_)); - - PackageEntries entries{ - { "new_data", "" }, - { "patch_data", "" }, - { "transfer_list", android::base::Join(transfer_list_fail, '\n') }, - }; - - // "2\nstash " + block3_hash + " 2,2,3" - std::string last_command_content = - "2\n" + transfer_list_fail[TransferList::kTransferListHeaderLines + 2]; - - RunBlockImageUpdate(false, entries, image_file_, ""); - - // Expect last_command to contain the last stash command. - std::string last_command_actual; - ASSERT_TRUE(android::base::ReadFileToString(last_command_file_, &last_command_actual)); - EXPECT_EQ(last_command_content, last_command_actual); - - std::string updated_contents; - ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated_contents)); - ASSERT_EQ(block1 + block1 + block3, updated_contents); - - // "Resume" the update. Expect the first 'move' to be skipped but the second 'move' to be - // executed. Note that we intentionally reset the image file. - entries["transfer_list"] = android::base::Join(transfer_list_continue, '\n'); - ASSERT_TRUE(android::base::WriteStringToFile(block1 + block2 + block3, image_file_)); - RunBlockImageUpdate(false, entries, image_file_, "t"); - - ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated_contents)); - ASSERT_EQ(block1 + block2 + block1, updated_contents); -} - -TEST_F(UpdaterTest, last_command_update_unresumable) { - std::string block1(4096, '1'); - std::string block2(4096, '2'); - std::string block1_hash = GetSha1(block1); - std::string block2_hash = GetSha1(block2); - - // Construct an unresumable update with source blocks mismatch. - std::vector<std::string> transfer_list_unresumable{ - // clang-format off - "4", - "2", - "0", - "2", - "stash " + block1_hash + " 2,0,1", - "move " + block2_hash + " 2,1,2 1 2,0,1", - // clang-format on - }; - - PackageEntries entries{ - { "new_data", "" }, - { "patch_data", "" }, - { "transfer_list", android::base::Join(transfer_list_unresumable, '\n') }, - }; - - ASSERT_TRUE(android::base::WriteStringToFile(block1 + block1, image_file_)); - - std::string last_command_content = - "0\n" + transfer_list_unresumable[TransferList::kTransferListHeaderLines]; - ASSERT_TRUE(android::base::WriteStringToFile(last_command_content, last_command_file_)); - - RunBlockImageUpdate(false, entries, image_file_, ""); - - // The last_command_file will be deleted if the update encounters an unresumable failure later. - ASSERT_EQ(-1, access(last_command_file_.c_str(), R_OK)); -} - -TEST_F(UpdaterTest, last_command_verify) { - std::string block1(4096, '1'); - std::string block2(4096, '2'); - std::string block3(4096, '3'); - std::string block1_hash = GetSha1(block1); - std::string block2_hash = GetSha1(block2); - std::string block3_hash = GetSha1(block3); - - std::vector<std::string> transfer_list_verify{ - // clang-format off - "4", - "2", - "0", - "2", - "stash " + block1_hash + " 2,0,1", - "move " + block1_hash + " 2,0,1 1 2,0,1", - "move " + block1_hash + " 2,1,2 1 2,0,1", - "stash " + block3_hash + " 2,2,3", - // clang-format on - }; - - PackageEntries entries{ - { "new_data", "" }, - { "patch_data", "" }, - { "transfer_list", android::base::Join(transfer_list_verify, '\n') }, - }; - - ASSERT_TRUE(android::base::WriteStringToFile(block1 + block1 + block3, image_file_)); - - // Last command: "move " + block1_hash + " 2,1,2 1 2,0,1" - std::string last_command_content = - "2\n" + transfer_list_verify[TransferList::kTransferListHeaderLines + 2]; - - // First run: expect the verification to succeed and the last_command_file is intact. - ASSERT_TRUE(android::base::WriteStringToFile(last_command_content, last_command_file_)); - - RunBlockImageUpdate(true, entries, image_file_, "t"); - - std::string last_command_actual; - ASSERT_TRUE(android::base::ReadFileToString(last_command_file_, &last_command_actual)); - EXPECT_EQ(last_command_content, last_command_actual); - - // Second run with a mismatching block image: expect the verification to succeed but - // last_command_file to be deleted; because the target blocks in the last command don't have the - // expected contents for the second move command. - ASSERT_TRUE(android::base::WriteStringToFile(block1 + block2 + block3, image_file_)); - RunBlockImageUpdate(true, entries, image_file_, "t"); - ASSERT_EQ(-1, access(last_command_file_.c_str(), R_OK)); -} - -class ResumableUpdaterTest : public testing::TestWithParam<size_t> { - protected: - void SetUp() override { - RegisterBuiltins(); - RegisterInstallFunctions(); - RegisterBlockImageFunctions(); - - Paths::Get().set_cache_temp_source(temp_saved_source_.path); - Paths::Get().set_last_command_file(temp_last_command_.path); - Paths::Get().set_stash_directory_base(temp_stash_base_.path); - - // Enable a special command "abort" to simulate interruption. - Command::abort_allowed_ = true; - - index_ = GetParam(); - image_file_ = image_temp_file_.path; - last_command_file_ = temp_last_command_.path; - } - - void TearDown() override { - // Clean up the last_command_file if any. - ASSERT_TRUE(android::base::RemoveFileIfExists(last_command_file_)); - - // Clear partition updated marker if any. - std::string updated_marker{ temp_stash_base_.path }; - updated_marker += "/" + GetSha1(image_temp_file_.path) + ".UPDATED"; - ASSERT_TRUE(android::base::RemoveFileIfExists(updated_marker)); - } - - TemporaryFile temp_saved_source_; - TemporaryDir temp_stash_base_; - std::string last_command_file_; - std::string image_file_; - size_t index_; - - private: - TemporaryFile temp_last_command_; - TemporaryFile image_temp_file_; -}; - -static std::string g_source_image; -static std::string g_target_image; -static PackageEntries g_entries; - -static std::vector<std::string> GenerateTransferList() { - std::string a(4096, 'a'); - std::string b(4096, 'b'); - std::string c(4096, 'c'); - std::string d(4096, 'd'); - std::string e(4096, 'e'); - std::string f(4096, 'f'); - std::string g(4096, 'g'); - std::string h(4096, 'h'); - std::string i(4096, 'i'); - std::string zero(4096, '\0'); - - std::string a_hash = GetSha1(a); - std::string b_hash = GetSha1(b); - std::string c_hash = GetSha1(c); - std::string e_hash = GetSha1(e); - - auto loc = [](const std::string& range_text) { - std::vector<std::string> pieces = android::base::Split(range_text, "-"); - size_t left; - size_t right; - if (pieces.size() == 1) { - CHECK(android::base::ParseUint(pieces[0], &left)); - right = left + 1; - } else { - CHECK_EQ(2u, pieces.size()); - CHECK(android::base::ParseUint(pieces[0], &left)); - CHECK(android::base::ParseUint(pieces[1], &right)); - right++; - } - return android::base::StringPrintf("2,%zu,%zu", left, right); - }; - - // patch 1: "b d c" -> "g" - TemporaryFile patch_file_bdc_g; - std::string bdc = b + d + c; - std::string bdc_hash = GetSha1(bdc); - std::string g_hash = GetSha1(g); - CHECK_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(bdc.data()), bdc.size(), - reinterpret_cast<const uint8_t*>(g.data()), g.size(), - patch_file_bdc_g.path, nullptr)); - std::string patch_bdc_g; - CHECK(android::base::ReadFileToString(patch_file_bdc_g.path, &patch_bdc_g)); - - // patch 2: "a b c d" -> "d c b" - TemporaryFile patch_file_abcd_dcb; - std::string abcd = a + b + c + d; - std::string abcd_hash = GetSha1(abcd); - std::string dcb = d + c + b; - std::string dcb_hash = GetSha1(dcb); - CHECK_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(abcd.data()), abcd.size(), - reinterpret_cast<const uint8_t*>(dcb.data()), dcb.size(), - patch_file_abcd_dcb.path, nullptr)); - std::string patch_abcd_dcb; - CHECK(android::base::ReadFileToString(patch_file_abcd_dcb.path, &patch_abcd_dcb)); - - std::vector<std::string> transfer_list{ - "4", - "10", // total blocks written - "2", // maximum stash entries - "2", // maximum number of stashed blocks - - // a b c d e a b c d e - "stash " + b_hash + " " + loc("1"), - // a b c d e a b c d e [b(1)] - "stash " + c_hash + " " + loc("2"), - // a b c d e a b c d e [b(1)][c(2)] - "new " + loc("1-2"), - // a i h d e a b c d e [b(1)][c(2)] - "zero " + loc("0"), - // 0 i h d e a b c d e [b(1)][c(2)] - - // bsdiff "b d c" (from stash, 3, stash) to get g(3) - android::base::StringPrintf( - "bsdiff 0 %zu %s %s %s 3 %s %s %s:%s %s:%s", - patch_bdc_g.size(), // patch start (0), patch length - bdc_hash.c_str(), // source hash - g_hash.c_str(), // target hash - loc("3").c_str(), // target range - loc("3").c_str(), loc("1").c_str(), // load "d" from block 3, into buffer at offset 1 - b_hash.c_str(), loc("0").c_str(), // load "b" from stash, into buffer at offset 0 - c_hash.c_str(), loc("2").c_str()), // load "c" from stash, into buffer at offset 2 - - // 0 i h g e a b c d e [b(1)][c(2)] - "free " + b_hash, - // 0 i h g e a b c d e [c(2)] - "free " + a_hash, - // 0 i h g e a b c d e - "stash " + a_hash + " " + loc("5"), - // 0 i h g e a b c d e [a(5)] - "move " + e_hash + " " + loc("5") + " 1 " + loc("4"), - // 0 i h g e e b c d e [a(5)] - - // bsdiff "a b c d" (from stash, 6-8) to "d c b" (6-8) - android::base::StringPrintf( // - "bsdiff %zu %zu %s %s %s 4 %s %s %s:%s", - patch_bdc_g.size(), // patch start - patch_bdc_g.size() + patch_abcd_dcb.size(), // patch length - abcd_hash.c_str(), // source hash - dcb_hash.c_str(), // target hash - loc("6-8").c_str(), // target range - loc("6-8").c_str(), // load "b c d" from blocks 6-8 - loc("1-3").c_str(), // into buffer at offset 1-3 - a_hash.c_str(), // load "a" from stash - loc("0").c_str()), // into buffer at offset 0 - - // 0 i h g e e d c b e [a(5)] - "new " + loc("4"), - // 0 i h g f e d c b e [a(5)] - "move " + a_hash + " " + loc("9") + " 1 - " + a_hash + ":" + loc("0"), - // 0 i h g f e d c b a [a(5)] - "free " + a_hash, - // 0 i h g f e d c b a - }; - - std::string new_data = i + h + f; - std::string patch_data = patch_bdc_g + patch_abcd_dcb; - - g_entries = { - { "new_data", new_data }, - { "patch_data", patch_data }, - }; - g_source_image = a + b + c + d + e + a + b + c + d + e; - g_target_image = zero + i + h + g + f + e + d + c + b + a; - - return transfer_list; -} - -static const std::vector<std::string> g_transfer_list = GenerateTransferList(); - -INSTANTIATE_TEST_CASE_P(InterruptAfterEachCommand, ResumableUpdaterTest, - ::testing::Range(static_cast<size_t>(0), - g_transfer_list.size() - - TransferList::kTransferListHeaderLines)); - -TEST_P(ResumableUpdaterTest, InterruptVerifyResume) { - ASSERT_TRUE(android::base::WriteStringToFile(g_source_image, image_file_)); - - LOG(INFO) << "Interrupting at line " << index_ << " (" - << g_transfer_list[TransferList::kTransferListHeaderLines + index_] << ")"; - - std::vector<std::string> transfer_list_copy{ g_transfer_list }; - transfer_list_copy[TransferList::kTransferListHeaderLines + index_] = "abort"; - - g_entries["transfer_list"] = android::base::Join(transfer_list_copy, '\n'); - - // Run update that's expected to fail. - RunBlockImageUpdate(false, g_entries, image_file_, ""); - - std::string last_command_expected; - - // Assert the last_command_file. - if (index_ == 0) { - ASSERT_EQ(-1, access(last_command_file_.c_str(), R_OK)); - } else { - last_command_expected = std::to_string(index_ - 1) + "\n" + - g_transfer_list[TransferList::kTransferListHeaderLines + index_ - 1]; - std::string last_command_actual; - ASSERT_TRUE(android::base::ReadFileToString(last_command_file_, &last_command_actual)); - ASSERT_EQ(last_command_expected, last_command_actual); - } - - g_entries["transfer_list"] = android::base::Join(g_transfer_list, '\n'); - - // Resume the interrupted update, by doing verification first. - RunBlockImageUpdate(true, g_entries, image_file_, "t"); - - // last_command_file should remain intact. - if (index_ == 0) { - ASSERT_EQ(-1, access(last_command_file_.c_str(), R_OK)); - } else { - std::string last_command_actual; - ASSERT_TRUE(android::base::ReadFileToString(last_command_file_, &last_command_actual)); - ASSERT_EQ(last_command_expected, last_command_actual); - } - - // Resume the update. - RunBlockImageUpdate(false, g_entries, image_file_, "t"); - - // last_command_file should be gone after successful update. - ASSERT_EQ(-1, access(last_command_file_.c_str(), R_OK)); - - std::string updated_image_actual; - ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated_image_actual)); - ASSERT_EQ(g_target_image, updated_image_actual); -} diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp deleted file mode 100644 index ded23c52f..000000000 --- a/tests/component/verifier_test.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agree to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include <string> -#include <vector> - -#include <android-base/file.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> -#include <android-base/unique_fd.h> -#include <gtest/gtest.h> -#include <openssl/bn.h> -#include <openssl/ec.h> -#include <openssl/nid.h> -#include <ziparchive/zip_writer.h> - -#include "common/test_constants.h" -#include "install/package.h" -#include "install/verifier.h" -#include "otautil/sysutil.h" - -using namespace std::string_literals; - -static void LoadKeyFromFile(const std::string& file_name, Certificate* cert) { - std::string testkey_string; - ASSERT_TRUE(android::base::ReadFileToString(file_name, &testkey_string)); - ASSERT_TRUE(LoadCertificateFromBuffer( - std::vector<uint8_t>(testkey_string.begin(), testkey_string.end()), cert)); -} - -static void VerifyFile(const std::string& content, const std::vector<Certificate>& keys, - int expected) { - auto package = - Package::CreateMemoryPackage(std::vector<uint8_t>(content.begin(), content.end()), nullptr); - ASSERT_NE(nullptr, package); - - ASSERT_EQ(expected, verify_file(package.get(), keys)); -} - -static void VerifyPackageWithCertificates(const std::string& name, - const std::vector<Certificate>& certs) { - std::string path = from_testdata_base(name); - auto package = Package::CreateMemoryPackage(path, nullptr); - ASSERT_NE(nullptr, package); - - ASSERT_EQ(VERIFY_SUCCESS, verify_file(package.get(), certs)); -} - -static void VerifyPackageWithSingleCertificate(const std::string& name, Certificate&& cert) { - std::vector<Certificate> certs; - certs.emplace_back(std::move(cert)); - VerifyPackageWithCertificates(name, certs); -} - -static void BuildCertificateArchive(const std::vector<std::string>& file_names, int fd) { - FILE* zip_file_ptr = fdopen(fd, "wb"); - ZipWriter zip_writer(zip_file_ptr); - - for (const auto& name : file_names) { - std::string content; - ASSERT_TRUE(android::base::ReadFileToString(name, &content)); - - // Makes sure the zip entry name has the correct suffix. - std::string entry_name = name; - if (!android::base::EndsWith(entry_name, "x509.pem")) { - entry_name += "x509.pem"; - } - ASSERT_EQ(0, zip_writer.StartEntry(entry_name.c_str(), ZipWriter::kCompress)); - ASSERT_EQ(0, zip_writer.WriteBytes(content.data(), content.size())); - ASSERT_EQ(0, zip_writer.FinishEntry()); - } - - ASSERT_EQ(0, zip_writer.Finish()); - ASSERT_EQ(0, fclose(zip_file_ptr)); -} - -TEST(VerifierTest, LoadCertificateFromBuffer_failure) { - Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); - std::string testkey_string; - ASSERT_TRUE( - android::base::ReadFileToString(from_testdata_base("testkey_v1.txt"), &testkey_string)); - ASSERT_FALSE(LoadCertificateFromBuffer( - std::vector<uint8_t>(testkey_string.begin(), testkey_string.end()), &cert)); -} - -TEST(VerifierTest, LoadCertificateFromBuffer_sha1_exponent3) { - Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); - LoadKeyFromFile(from_testdata_base("testkey_v1.x509.pem"), &cert); - - ASSERT_EQ(SHA_DIGEST_LENGTH, cert.hash_len); - ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); - ASSERT_EQ(nullptr, cert.ec); - - VerifyPackageWithSingleCertificate("otasigned_v1.zip", std::move(cert)); -} - -TEST(VerifierTest, LoadCertificateFromBuffer_sha1_exponent65537) { - Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); - LoadKeyFromFile(from_testdata_base("testkey_v2.x509.pem"), &cert); - - ASSERT_EQ(SHA_DIGEST_LENGTH, cert.hash_len); - ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); - ASSERT_EQ(nullptr, cert.ec); - - VerifyPackageWithSingleCertificate("otasigned_v2.zip", std::move(cert)); -} - -TEST(VerifierTest, LoadCertificateFromBuffer_sha256_exponent3) { - Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); - LoadKeyFromFile(from_testdata_base("testkey_v3.x509.pem"), &cert); - - ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); - ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); - ASSERT_EQ(nullptr, cert.ec); - - VerifyPackageWithSingleCertificate("otasigned_v3.zip", std::move(cert)); -} - -TEST(VerifierTest, LoadCertificateFromBuffer_sha256_exponent65537) { - Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); - LoadKeyFromFile(from_testdata_base("testkey_v4.x509.pem"), &cert); - - ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); - ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); - ASSERT_EQ(nullptr, cert.ec); - - VerifyPackageWithSingleCertificate("otasigned_v4.zip", std::move(cert)); -} - -TEST(VerifierTest, LoadCertificateFromBuffer_sha256_ec256bits) { - Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); - LoadKeyFromFile(from_testdata_base("testkey_v5.x509.pem"), &cert); - - ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); - ASSERT_EQ(Certificate::KEY_TYPE_EC, cert.key_type); - ASSERT_EQ(nullptr, cert.rsa); - - VerifyPackageWithSingleCertificate("otasigned_v5.zip", std::move(cert)); -} - -TEST(VerifierTest, LoadCertificateFromBuffer_sha256_rsa4096_bits) { - Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); - LoadKeyFromFile(from_testdata_base("testkey_4096bits.x509.pem"), &cert); - - ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); - ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); - ASSERT_EQ(nullptr, cert.ec); - - VerifyPackageWithSingleCertificate("otasigned_4096bits.zip", std::move(cert)); -} - -TEST(VerifierTest, LoadCertificateFromBuffer_check_rsa_keys) { - std::unique_ptr<RSA, RSADeleter> rsa(RSA_new()); - std::unique_ptr<BIGNUM, decltype(&BN_free)> exponent(BN_new(), BN_free); - BN_set_word(exponent.get(), 3); - RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr); - ASSERT_TRUE(CheckRSAKey(rsa)); - - // Exponent is expected to be 3 or 65537 - BN_set_word(exponent.get(), 17); - RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr); - ASSERT_FALSE(CheckRSAKey(rsa)); - - // Modulus is expected to be 2048. - BN_set_word(exponent.get(), 3); - RSA_generate_key_ex(rsa.get(), 1024, exponent.get(), nullptr); - ASSERT_FALSE(CheckRSAKey(rsa)); -} - -TEST(VerifierTest, LoadCertificateFromBuffer_check_ec_keys) { - std::unique_ptr<EC_KEY, ECKEYDeleter> ec(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); - ASSERT_EQ(1, EC_KEY_generate_key(ec.get())); - ASSERT_TRUE(CheckECKey(ec)); - - // Expects 256-bit EC key with curve NIST P-256 - ec.reset(EC_KEY_new_by_curve_name(NID_secp224r1)); - ASSERT_EQ(1, EC_KEY_generate_key(ec.get())); - ASSERT_FALSE(CheckECKey(ec)); -} - -TEST(VerifierTest, LoadKeysFromZipfile_empty_archive) { - TemporaryFile otacerts; - BuildCertificateArchive({}, otacerts.release()); - std::vector<Certificate> certs = LoadKeysFromZipfile(otacerts.path); - ASSERT_TRUE(certs.empty()); -} - -TEST(VerifierTest, LoadKeysFromZipfile_single_key) { - TemporaryFile otacerts; - BuildCertificateArchive({ from_testdata_base("testkey_v1.x509.pem") }, otacerts.release()); - std::vector<Certificate> certs = LoadKeysFromZipfile(otacerts.path); - ASSERT_EQ(1, certs.size()); - - VerifyPackageWithCertificates("otasigned_v1.zip", certs); -} - -TEST(VerifierTest, LoadKeysFromZipfile_corrupted_key) { - TemporaryFile corrupted_key; - std::string content; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v1.x509.pem"), &content)); - content = "random-contents" + content; - ASSERT_TRUE(android::base::WriteStringToFd(content, corrupted_key.release())); - - TemporaryFile otacerts; - BuildCertificateArchive({ from_testdata_base("testkey_v2.x509.pem"), corrupted_key.path }, - otacerts.release()); - std::vector<Certificate> certs = LoadKeysFromZipfile(otacerts.path); - ASSERT_EQ(0, certs.size()); -} - -TEST(VerifierTest, LoadKeysFromZipfile_multiple_key) { - TemporaryFile otacerts; - BuildCertificateArchive( - { - from_testdata_base("testkey_v3.x509.pem"), - from_testdata_base("testkey_v4.x509.pem"), - from_testdata_base("testkey_v5.x509.pem"), - - }, - otacerts.release()); - std::vector<Certificate> certs = LoadKeysFromZipfile(otacerts.path); - ASSERT_EQ(3, certs.size()); - - VerifyPackageWithCertificates("otasigned_v3.zip", certs); - VerifyPackageWithCertificates("otasigned_v4.zip", certs); - VerifyPackageWithCertificates("otasigned_v5.zip", certs); -} - -class VerifierTest : public testing::TestWithParam<std::vector<std::string>> { - protected: - void SetUp() override { - std::vector<std::string> args = GetParam(); - std::string path = from_testdata_base(args[0]); - memory_package_ = Package::CreateMemoryPackage(path, nullptr); - ASSERT_NE(nullptr, memory_package_); - file_package_ = Package::CreateFilePackage(path, nullptr); - ASSERT_NE(nullptr, file_package_); - - for (auto it = ++args.cbegin(); it != args.cend(); ++it) { - std::string public_key_file = from_testdata_base("testkey_" + *it + ".x509.pem"); - certs_.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); - LoadKeyFromFile(public_key_file, &certs_.back()); - } - } - - std::unique_ptr<Package> memory_package_; - std::unique_ptr<Package> file_package_; - std::vector<Certificate> certs_; -}; - -class VerifierSuccessTest : public VerifierTest { -}; - -class VerifierFailureTest : public VerifierTest { -}; - -TEST(VerifierTest, BadPackage_AlteredFooter) { - std::vector<Certificate> certs; - certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); - LoadKeyFromFile(from_testdata_base("testkey_v3.x509.pem"), &certs.back()); - - std::string package; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("otasigned_v3.zip"), &package)); - ASSERT_EQ(std::string("\xc0\x06\xff\xff\xd2\x06", 6), package.substr(package.size() - 6, 6)); - - // Alter the footer. - package[package.size() - 5] = '\x05'; - VerifyFile(package, certs, VERIFY_FAILURE); -} - -TEST(VerifierTest, BadPackage_AlteredContent) { - std::vector<Certificate> certs; - certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); - LoadKeyFromFile(from_testdata_base("testkey_v3.x509.pem"), &certs.back()); - - std::string package; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("otasigned_v3.zip"), &package)); - ASSERT_GT(package.size(), static_cast<size_t>(100)); - - // Alter the content. - std::string altered1(package); - altered1[50] += 1; - VerifyFile(altered1, certs, VERIFY_FAILURE); - - std::string altered2(package); - altered2[10] += 1; - VerifyFile(altered2, certs, VERIFY_FAILURE); -} - -TEST(VerifierTest, BadPackage_SignatureStartOutOfBounds) { - std::vector<Certificate> certs; - certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); - LoadKeyFromFile(from_testdata_base("testkey_v3.x509.pem"), &certs.back()); - - // Signature start is 65535 (0xffff) while comment size is 0 (Bug: 31914369). - std::string package = "\x50\x4b\x05\x06"s + std::string(12, '\0') + "\xff\xff\xff\xff\x00\x00"s; - VerifyFile(package, certs, VERIFY_FAILURE); -} - -TEST_P(VerifierSuccessTest, VerifySucceed) { - ASSERT_EQ(VERIFY_SUCCESS, verify_file(memory_package_.get(), certs_)); - ASSERT_EQ(VERIFY_SUCCESS, verify_file(file_package_.get(), certs_)); -} - -TEST_P(VerifierFailureTest, VerifyFailure) { - ASSERT_EQ(VERIFY_FAILURE, verify_file(memory_package_.get(), certs_)); - ASSERT_EQ(VERIFY_FAILURE, verify_file(file_package_.get(), certs_)); -} - -INSTANTIATE_TEST_CASE_P(SingleKeySuccess, VerifierSuccessTest, - ::testing::Values( - std::vector<std::string>({"otasigned_v1.zip", "v1"}), - std::vector<std::string>({"otasigned_v2.zip", "v2"}), - std::vector<std::string>({"otasigned_v3.zip", "v3"}), - std::vector<std::string>({"otasigned_v4.zip", "v4"}), - std::vector<std::string>({"otasigned_v5.zip", "v5"}))); - -INSTANTIATE_TEST_CASE_P(MultiKeySuccess, VerifierSuccessTest, - ::testing::Values( - std::vector<std::string>({"otasigned_v1.zip", "v1", "v2"}), - std::vector<std::string>({"otasigned_v2.zip", "v5", "v2"}), - std::vector<std::string>({"otasigned_v3.zip", "v5", "v1", "v3"}), - std::vector<std::string>({"otasigned_v4.zip", "v5", "v1", "v4"}), - std::vector<std::string>({"otasigned_v5.zip", "v4", "v1", "v5"}))); - -INSTANTIATE_TEST_CASE_P(WrongKey, VerifierFailureTest, - ::testing::Values( - std::vector<std::string>({"otasigned_v1.zip", "v2"}), - std::vector<std::string>({"otasigned_v2.zip", "v1"}), - std::vector<std::string>({"otasigned_v3.zip", "v5"}), - std::vector<std::string>({"otasigned_v4.zip", "v5"}), - std::vector<std::string>({"otasigned_v5.zip", "v3"}))); - -INSTANTIATE_TEST_CASE_P(WrongHash, VerifierFailureTest, - ::testing::Values( - std::vector<std::string>({"otasigned_v1.zip", "v3"}), - std::vector<std::string>({"otasigned_v2.zip", "v4"}), - std::vector<std::string>({"otasigned_v3.zip", "v1"}), - std::vector<std::string>({"otasigned_v4.zip", "v2"}))); - -INSTANTIATE_TEST_CASE_P(BadPackage, VerifierFailureTest, - ::testing::Values( - std::vector<std::string>({"random.zip", "v1"}), - std::vector<std::string>({"fake-eocd.zip", "v1"}))); |