applypatch: Change applypatch command-line arguments.

This applies to the standalone applypatch executable
(/system/bin/applypatch on device). This executable is only used when
installing (via patching or flashing) a recovery image on non-A/B
device.

This CL removes the support for patching non-eMMC targets from
applypatch that has been deprecated as part of file-based OTA. For
patching eMMC targets, it also drops the support for accepting multiple
patches (not useful, since the source file must be fixed).

This CL needs the matching change in the same topic, which writes the
script of "/system/bin/install-recovery.sh". Note that this CL doesn't
chanage the applypatch API signatures, in order to minimize the CL size.

*BEFORE*

usage: /system/bin/applypatch [-b <bonus-file>] <src-file> <tgt-file> <tgt-sha1> <tgt-size> [<src-sha1>:<patch> ...]
   or  /system/bin/applypatch -c <file> [<sha1> ...]
   or  /system/bin/applypatch -l

Filenames may be of the form
  EMMC:<partition>:<len_1>:<sha1_1>:<len_2>:<sha1_2>:...
to specify reading from or writing to an EMMC partition.

*AFTER*

Usage:
check mode
  applypatch --check EMMC:<target-file>:<target-size>:<target-sha1>

flash mode
  applypatch --flash <source-file>
             --target EMMC:<target-file>:<target-size>:<target-sha1>

patch mode
  applypatch [--bonus <bonus-file>]
             --patch <patch-file>
             --target EMMC:<target-file>:<target-size>:<target-sha1>
             --source EMMC:<source-file>:<source-size>:<source-sha1>

show license
  applypatch --license

Bug: 110106408
Test: Run recovery_component_test and recovery_unit_test on marlin.
Test: Build a non-A/B target that has /system/bin/install-recovery.sh.
      Verify that it installs recovery image successfully.
Test: Build a non-A/B target that has /system/bin/install-recovery.sh in
      flashing mode. Verify that it installs recovery image successfully.
Change-Id: I71f9a71fb457e6f663e0b5511946949e65b4b78c
This commit is contained in:
Tao Bao 2018-07-13 13:11:09 -07:00
parent 29932e7bcc
commit d34e6bc44b
6 changed files with 259 additions and 345 deletions

View file

@ -495,19 +495,15 @@ int applypatch_flash(const char* source_filename, const char* target_filename,
return 1;
}
std::string target_str(target_filename);
std::vector<std::string> pieces = android::base::Split(target_str, ":");
if (pieces.size() != 2 || pieces[0] != "EMMC") {
std::vector<std::string> pieces = android::base::Split(target_filename, ":");
if (pieces.size() != 4 || pieces[0] != "EMMC") {
LOG(ERROR) << "Invalid target name \"" << target_filename << "\"";
return 1;
}
// Load the target into the source_file object to see if already applied.
pieces.push_back(std::to_string(target_size));
pieces.push_back(target_sha1_str);
std::string fullname = android::base::Join(pieces, ':');
FileContents source_file;
if (LoadPartitionContents(fullname, &source_file) == 0 &&
if (LoadPartitionContents(target_filename, &source_file) == 0 &&
memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) {
// The early-exit case: the image was already applied, this partition has the desired hash,
// nothing for us to do.

View file

@ -16,13 +16,7 @@
#include "applypatch_modes.h"
// This program (applypatch) applies binary patches to files in a way that
// is safe (the original file is not touched until we have the desired
// replacement for it) and idempotent (it's okay to run this program
// multiple times).
//
// See the comments to applypatch_modes() function.
// See the comments for applypatch() function.
int main(int argc, char** argv) {
return applypatch_modes(argc, const_cast<const char**>(argv));
return applypatch_modes(argc, argv);
}

View file

@ -16,6 +16,7 @@
#include "applypatch_modes.h"
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -25,6 +26,7 @@
#include <string>
#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
@ -33,157 +35,162 @@
#include "applypatch/applypatch.h"
#include "edify/expr.h"
static int CheckMode(int argc, const char** argv) {
if (argc < 3) {
return 2;
}
std::vector<std::string> sha1;
for (int i = 3; i < argc; i++) {
sha1.push_back(argv[i]);
}
return applypatch_check(argv[2], sha1);
static int CheckMode(const std::string& target) {
return applypatch_check(target, {});
}
// Parse arguments (which should be of the form "<sha1>:<filename>" into the
// new parallel arrays *sha1s and *files. Returns true on success.
static bool ParsePatchArgs(int argc, const char** argv, std::vector<std::string>* sha1s,
std::vector<FileContents>* files) {
if (sha1s == nullptr) {
return false;
}
for (int i = 0; i < argc; ++i) {
std::vector<std::string> pieces = android::base::Split(argv[i], ":");
if (pieces.size() != 2) {
LOG(ERROR) << "Failed to parse patch argument \"" << argv[i] << "\"";
return false;
}
uint8_t digest[SHA_DIGEST_LENGTH];
if (ParseSha1(pieces[0], digest) != 0) {
LOG(ERROR) << "Failed to parse SHA-1 \"" << argv[i] << "\"";
return false;
}
sha1s->push_back(pieces[0]);
FileContents fc;
if (LoadFileContents(pieces[1].c_str(), &fc) != 0) {
return false;
}
files->push_back(std::move(fc));
}
return true;
}
static int FlashMode(const char* src_filename, const char* tgt_filename,
const char* tgt_sha1, size_t tgt_size) {
return applypatch_flash(src_filename, tgt_filename, tgt_sha1, tgt_size);
}
static int PatchMode(int argc, const char** argv) {
std::unique_ptr<Value> bonus;
if (argc >= 3 && strcmp(argv[1], "-b") == 0) {
FileContents bonus_fc;
if (LoadFileContents(argv[2], &bonus_fc) != 0) {
LOG(ERROR) << "Failed to load bonus file " << argv[2];
return 1;
}
bonus = std::make_unique<Value>(Value::Type::BLOB,
std::string(bonus_fc.data.cbegin(), bonus_fc.data.cend()));
argc -= 2;
argv += 2;
static int FlashMode(const std::string& target_emmc, const std::string& source_file) {
std::vector<std::string> pieces = android::base::Split(target_emmc, ":");
if (pieces.size() != 4 || pieces[0] != "EMMC") {
return 2;
}
size_t target_size;
if (!android::base::ParseUint(pieces[2], &target_size) || target_size == 0) {
LOG(ERROR) << "Failed to parse \"" << pieces[2] << "\" as byte count";
return 1;
}
return applypatch_flash(source_file.c_str(), target_emmc.c_str(), pieces[3].c_str(), target_size);
}
if (argc < 4) {
static int PatchMode(const std::string& target_emmc, const std::string& source_emmc,
const std::string& patch_file, const std::string& bonus_file) {
std::vector<std::string> target_pieces = android::base::Split(target_emmc, ":");
if (target_pieces.size() != 4 || target_pieces[0] != "EMMC") {
return 2;
}
size_t target_size;
if (!android::base::ParseUint(argv[4], &target_size) || target_size == 0) {
LOG(ERROR) << "Failed to parse \"" << argv[4] << "\" as byte count";
if (!android::base::ParseUint(target_pieces[2], &target_size) || target_size == 0) {
LOG(ERROR) << "Failed to parse \"" << target_pieces[2] << "\" as byte count";
return 1;
}
// If no <src-sha1>:<patch> is provided, it is in flash mode.
if (argc == 5) {
if (bonus) {
LOG(ERROR) << "bonus file not supported in flash mode";
return 1;
}
return FlashMode(argv[1], argv[2], argv[3], target_size);
}
std::vector<std::string> sha1s;
std::vector<FileContents> files;
if (!ParsePatchArgs(argc - 5, argv + 5, &sha1s, &files)) {
LOG(ERROR) << "Failed to parse patch args";
return 1;
}
std::vector<std::unique_ptr<Value>> patches;
for (const auto& file : files) {
patches.push_back(std::make_unique<Value>(Value::Type::BLOB,
std::string(file.data.cbegin(), file.data.cend())));
}
return applypatch(argv[1], argv[2], argv[3], target_size, sha1s, patches, bonus.get());
}
// This program (applypatch) applies binary patches to files in a way that
// is safe (the original file is not touched until we have the desired
// replacement for it) and idempotent (it's okay to run this program
// multiple times).
//
// - if the sha1 hash of <tgt-file> is <tgt-sha1>, does nothing and exits
// successfully.
//
// - otherwise, if no <src-sha1>:<patch> is provided, flashes <tgt-file> with
// <src-file>. <tgt-file> must be a partition name, while <src-file> must
// be a regular image file. <src-file> will not be deleted on success.
//
// - otherwise, if the sha1 hash of <src-file> is <src-sha1>, applies the
// bsdiff <patch> to <src-file> to produce a new file (the type of patch
// is automatically detected from the file header). If that new
// file has sha1 hash <tgt-sha1>, moves it to replace <tgt-file>, and
// exits successfully. Note that if <src-file> and <tgt-file> are
// not the same, <src-file> is NOT deleted on success. <tgt-file>
// may be the string "-" to mean "the same as src-file".
//
// - otherwise, or if any error is encountered, exits with non-zero
// status.
//
// <src-file> (or <file> in check mode) may refer to an EMMC partition
// to read the source data. See the comments for the
// LoadPartitionContents() function for the format of such a filename.
int applypatch_modes(int argc, const char** argv) {
if (argc < 2) {
usage:
// clang-format off
LOG(INFO) << "Usage: \n"
<< " " << argv[0] << " [-b <bonus-file>] <src-file> <tgt-file> <tgt-sha1> "
"<tgt-size> [<src-sha1>:<patch> ...]\n"
<< " " << argv[0] << " -c <file> [<sha1> ...]\n"
<< " " << argv[0] << " -l\n"
<< "\n"
<< "Filenames may be of the form\n"
<< " EMMC:<partition>:<len_1>:<sha1_1>:<len_2>:<sha1_2>:...\n"
<< "to specify reading from or writing to an EMMC partition.\n\n";
// clang-format on
std::vector<std::string> source_pieces = android::base::Split(source_emmc, ":");
if (source_pieces.size() != 4 || source_pieces[0] != "EMMC") {
return 2;
}
int result;
if (strncmp(argv[1], "-l", 3) == 0) {
result = ShowLicenses();
} else if (strncmp(argv[1], "-c", 3) == 0) {
result = CheckMode(argc, argv);
} else {
result = PatchMode(argc, argv);
size_t source_size;
if (!android::base::ParseUint(source_pieces[2], &source_size) || source_size == 0) {
LOG(ERROR) << "Failed to parse \"" << source_pieces[2] << "\" as byte count";
return 1;
}
if (result == 2) {
goto usage;
std::string contents;
if (!android::base::ReadFileToString(patch_file, &contents)) {
PLOG(ERROR) << "Failed to read patch file \"" << patch_file << "\"";
return 1;
}
return result;
std::vector<std::unique_ptr<Value>> patches;
patches.push_back(std::make_unique<Value>(Value::Type::BLOB, std::move(contents)));
std::vector<std::string> sha1s{ source_pieces[3] };
std::unique_ptr<Value> bonus;
if (!bonus_file.empty()) {
std::string bonus_contents;
if (!android::base::ReadFileToString(bonus_file, &bonus_contents)) {
PLOG(ERROR) << "Failed to read bonus file \"" << bonus_file << "\"";
return 1;
}
bonus = std::make_unique<Value>(Value::Type::BLOB, std::move(bonus_contents));
}
return applypatch(source_emmc.c_str(), target_emmc.c_str(), target_pieces[3].c_str(), target_size,
sha1s, patches, bonus.get());
}
static void Usage() {
printf(
"Usage: \n"
"check mode\n"
" applypatch --check EMMC:<target-file>:<target-size>:<target-sha1>\n\n"
"flash mode\n"
" applypatch --flash <source-file>\n"
" --target EMMC:<target-file>:<target-size>:<target-sha1>\n\n"
"patch mode\n"
" applypatch [--bonus <bonus-file>]\n"
" --patch <patch-file>\n"
" --target EMMC:<target-file>:<target-size>:<target-sha1>\n"
" --source EMMC:<source-file>:<source-size>:<source-sha1>\n\n"
"show license\n"
" applypatch --license\n"
"\n\n");
}
int applypatch_modes(int argc, char* argv[]) {
static constexpr struct option OPTIONS[]{
// clang-format off
{ "bonus", required_argument, nullptr, 0 },
{ "check", required_argument, nullptr, 0 },
{ "flash", required_argument, nullptr, 0 },
{ "license", no_argument, nullptr, 0 },
{ "patch", required_argument, nullptr, 0 },
{ "source", required_argument, nullptr, 0 },
{ "target", required_argument, nullptr, 0 },
{ nullptr, 0, nullptr, 0 },
// clang-format on
};
std::string check_target;
std::string source;
std::string target;
std::string patch;
std::string bonus;
bool check_mode = false;
bool flash_mode = false;
bool patch_mode = false;
optind = 1;
int arg;
int option_index;
while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
switch (arg) {
case 0: {
std::string option = OPTIONS[option_index].name;
if (option == "bonus") {
bonus = optarg;
} else if (option == "check") {
check_target = optarg;
check_mode = true;
} else if (option == "flash") {
source = optarg;
flash_mode = true;
} else if (option == "license") {
return ShowLicenses();
} else if (option == "patch") {
patch = optarg;
patch_mode = true;
} else if (option == "source") {
source = optarg;
} else if (option == "target") {
target = optarg;
}
break;
}
case '?':
default:
LOG(ERROR) << "Invalid argument";
Usage();
return 2;
}
}
if (check_mode) {
return CheckMode(check_target);
}
if (flash_mode) {
if (!bonus.empty()) {
LOG(ERROR) << "bonus file not supported in flash mode";
return 1;
}
return FlashMode(target, source);
}
if (patch_mode) {
return PatchMode(target, source, patch, bonus);
}
Usage();
return 2;
}

View file

@ -17,6 +17,6 @@
#ifndef _APPLYPATCH_MODES_H
#define _APPLYPATCH_MODES_H
int applypatch_modes(int argc, const char** argv);
int applypatch_modes(int argc, char* argv[]);
#endif // _APPLYPATCH_MODES_H

View file

@ -75,10 +75,11 @@ int applypatch(const char* source_filename, const char* target_filename,
// 'sha1s' to find a match on /cache if the hashes embedded in the filename fail to match.
int applypatch_check(const std::string& filename, const std::vector<std::string>& sha1s);
// Flashes a given image to the target partition. It verifies the target cheksum first, and will
// return if target already has the desired hash. Otherwise it checks the checksum of the given
// source image before flashing, and verifies the target partition afterwards. The function is
// idempotent. Returns zero on success.
// Flashes a given image to the eMMC target partition. It verifies the target cheksum first, and
// will return if target already has the desired hash. Otherwise it checks the checksum of the
// given source image before flashing, and verifies the target partition afterwards.
// 'target_filename' must refer to an eMMC partition, of the form "EMMC:<device>:<size>:<hash>".
// The function is idempotent. Returns zero on success.
int applypatch_flash(const char* source_filename, const char* target_filename,
const char* target_sha1_str, size_t target_size);

View file

@ -21,7 +21,8 @@
#include <vector>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <android-base/test_utils.h>
#include <bsdiff/bsdiff.h>
#include <gtest/gtest.h>
@ -31,160 +32,112 @@
#include "common/test_constants.h"
#include "otautil/paths.h"
#include "otautil/print_sha1.h"
#include "otautil/sysutil.h"
using namespace std::string_literals;
static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = nullptr) {
ASSERT_TRUE(sha1 != nullptr);
// 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;
ASSERT_TRUE(android::base::ReadFileToString(fname, &data));
if (fsize != nullptr) {
*fsize = data.size();
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);
*sha1 = print_sha1(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 {
Paths::Get().set_cache_temp_source(cache_source.path);
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);
}
TemporaryFile cache_source;
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, applypatch_modes(1, (const char* []){ "applypatch" }));
ASSERT_EQ(2, InvokeApplyPatchModes({ "applypatch" }));
// Unrecognized args.
ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-x" }));
ASSERT_EQ(2, InvokeApplyPatchModes({ "applypatch", "-x" }));
}
TEST_F(ApplyPatchModesTest, PatchModeEmmcTarget) {
std::string boot_img = from_testdata_base("boot.img");
size_t boot_img_size;
std::string boot_img_sha1;
sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
std::string recovery_img = from_testdata_base("recovery.img");
size_t recovery_img_size;
std::string recovery_img_sha1;
sha1sum(recovery_img, &recovery_img_sha1, &recovery_img_size);
std::string recovery_img_size_arg = std::to_string(recovery_img_size);
std::string bonus_file = from_testdata_base("bonus.file");
// applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
std::string src_file_arg =
"EMMC:" + boot_img + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1;
TemporaryFile tgt_file;
std::string tgt_file_arg = "EMMC:"s + tgt_file.path;
std::string patch_arg = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p");
std::vector<const char*> args = { "applypatch",
"-b",
bonus_file.c_str(),
src_file_arg.c_str(),
tgt_file_arg.c_str(),
recovery_img_sha1.c_str(),
recovery_img_size_arg.c_str(),
patch_arg.c_str() };
ASSERT_EQ(0, applypatch_modes(args.size(), args.data()));
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 the EMMC target without a separate bonus file (i.e. recovery-from-boot patch has
// Tests patching an eMMC target without a separate bonus file (i.e. recovery-from-boot patch has
// everything).
TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithoutBonusFile) {
std::string boot_img = from_testdata_base("boot.img");
size_t boot_img_size;
std::string boot_img_sha1;
sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
std::vector<std::string> args{
"applypatch", "--patch", from_testdata_base("recovery-from-boot-with-bonus.p"),
"--target", target, "--source",
source,
};
std::string recovery_img = from_testdata_base("recovery.img");
size_t recovery_img_size;
std::string recovery_img_sha1;
sha1sum(recovery_img, &recovery_img_sha1, &recovery_img_size);
std::string recovery_img_size_arg = std::to_string(recovery_img_size);
// applypatch <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
std::string src_file_arg =
"EMMC:" + boot_img + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1;
TemporaryFile tgt_file;
std::string tgt_file_arg = "EMMC:"s + tgt_file.path;
std::string patch_arg =
boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p");
std::vector<const char*> args = { "applypatch",
src_file_arg.c_str(),
tgt_file_arg.c_str(),
recovery_img_sha1.c_str(),
recovery_img_size_arg.c_str(),
patch_arg.c_str() };
ASSERT_EQ(0, applypatch_modes(args.size(), args.data()));
}
TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithMultiplePatches) {
std::string boot_img = from_testdata_base("boot.img");
size_t boot_img_size;
std::string boot_img_sha1;
sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
std::string recovery_img = from_testdata_base("recovery.img");
size_t recovery_img_size;
std::string recovery_img_sha1;
sha1sum(recovery_img, &recovery_img_sha1, &recovery_img_size);
std::string recovery_img_size_arg = std::to_string(recovery_img_size);
std::string bonus_file = from_testdata_base("bonus.file");
// applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> \
// <src-sha1-fake1>:<patch1> <src-sha1>:<patch2> <src-sha1-fake2>:<patch3>
std::string src_file_arg =
"EMMC:" + boot_img + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1;
TemporaryFile tgt_file;
std::string tgt_file_arg = "EMMC:"s + tgt_file.path;
std::string bad_sha1_a = android::base::StringPrintf("%040x", rand());
std::string bad_sha1_b = android::base::StringPrintf("%040x", rand());
std::string patch1 = bad_sha1_a + ":" + from_testdata_base("recovery-from-boot.p");
std::string patch2 = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p");
std::string patch3 = bad_sha1_b + ":" + from_testdata_base("recovery-from-boot.p");
std::vector<const char*> args = { "applypatch",
"-b",
bonus_file.c_str(),
src_file_arg.c_str(),
tgt_file_arg.c_str(),
recovery_img_sha1.c_str(),
recovery_img_size_arg.c_str(),
patch1.c_str(),
patch2.c_str(),
patch3.c_str() };
ASSERT_EQ(0, applypatch_modes(args.size(), args.data()));
ASSERT_EQ(0, InvokeApplyPatchModes(args));
VerifyPatchedTarget(target);
}
// Ensures that applypatch works with a bsdiff based recovery-from-boot.p.
TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithBsdiffPatch) {
std::string boot_img_file = from_testdata_base("boot.img");
std::string boot_img_sha1;
size_t boot_img_size;
sha1sum(boot_img_file, &boot_img_sha1, &boot_img_size);
std::string recovery_img_file = from_testdata_base("recovery.img");
std::string recovery_img_sha1;
size_t recovery_img_size;
sha1sum(recovery_img_file, &recovery_img_sha1, &recovery_img_size);
// Generate the bsdiff patch of recovery-from-boot.p.
std::string src_content;
ASSERT_TRUE(android::base::ReadFileToString(boot_img_file, &src_content));
ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("boot.img"), &src_content));
std::string tgt_content;
ASSERT_TRUE(android::base::ReadFileToString(recovery_img_file, &tgt_content));
ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("recovery.img"), &tgt_content));
TemporaryFile patch_file;
ASSERT_EQ(0,
@ -192,92 +145,55 @@ TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithBsdiffPatch) {
reinterpret_cast<const uint8_t*>(tgt_content.data()), tgt_content.size(),
patch_file.path, nullptr));
// applypatch <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch>
std::string src_file_arg =
"EMMC:" + boot_img_file + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1;
TemporaryFile tgt_file;
std::string tgt_file_arg = "EMMC:"s + tgt_file.path;
std::string recovery_img_size_arg = std::to_string(recovery_img_size);
std::string patch_arg = boot_img_sha1 + ":" + patch_file.path;
std::vector<const char*> args = { "applypatch",
src_file_arg.c_str(),
tgt_file_arg.c_str(),
recovery_img_sha1.c_str(),
recovery_img_size_arg.c_str(),
patch_arg.c_str() };
ASSERT_EQ(0, applypatch_modes(args.size(), args.data()));
// Double check the patched recovery image.
std::string tgt_file_sha1;
size_t tgt_file_size;
sha1sum(tgt_file.path, &tgt_file_sha1, &tgt_file_size);
ASSERT_EQ(recovery_img_size, tgt_file_size);
ASSERT_EQ(recovery_img_sha1, tgt_file_sha1);
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.
ASSERT_NE(0, applypatch_modes(3, (const char* []){ "applypatch", "-b", "/doesntexist" }));
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));
std::string bonus_file = from_testdata_base("bonus.file");
// With bonus file, but missing args.
ASSERT_EQ(2, applypatch_modes(3, (const char* []){ "applypatch", "-b", bonus_file.c_str() }));
ASSERT_NE(0,
InvokeApplyPatchModes({ "applypatch", "--bonus", from_testdata_base("bonus.file") }));
}
std::string boot_img = from_testdata_base("boot.img");
size_t boot_img_size;
std::string boot_img_sha1;
sha1sum(boot_img, &boot_img_sha1, &boot_img_size);
std::string recovery_img = from_testdata_base("recovery.img");
size_t size;
std::string recovery_img_sha1;
sha1sum(recovery_img, &recovery_img_sha1, &size);
std::string recovery_img_size = std::to_string(size);
// Bonus file is not supported in flash mode.
// applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size>
TemporaryFile tmp4;
std::vector<const char*> args4 = {
"applypatch",
"-b",
bonus_file.c_str(),
boot_img.c_str(),
tmp4.path,
recovery_img_sha1.c_str(),
recovery_img_size.c_str(),
TEST_F(ApplyPatchModesTest, FlashMode) {
std::vector<std::string> args{
"applypatch", "--flash", from_testdata_base("recovery.img"), "--target", target,
};
ASSERT_NE(0, applypatch_modes(args4.size(), args4.data()));
ASSERT_EQ(0, InvokeApplyPatchModes(args));
VerifyPatchedTarget(target);
}
// Failed to parse patch args.
TemporaryFile tmp5;
std::string bad_arg1 =
"invalid-sha1:filename" + from_testdata_base("recovery-from-boot-with-bonus.p");
std::vector<const char*> args5 = {
"applypatch",
boot_img.c_str(),
tmp5.path,
recovery_img_sha1.c_str(),
recovery_img_size.c_str(),
bad_arg1.c_str(),
TEST_F(ApplyPatchModesTest, FlashModeInvalidArgs) {
std::vector<std::string> args{
"applypatch", "--bonus", from_testdata_base("bonus.file"), "--flash", source,
"--target", target,
};
ASSERT_NE(0, applypatch_modes(args5.size(), args5.data()));
ASSERT_NE(0, InvokeApplyPatchModes(args));
}
// Target size cannot be zero.
TemporaryFile tmp6;
std::string patch = boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p");
std::vector<const char*> args6 = {
"applypatch", boot_img.c_str(), tmp6.path, recovery_img_sha1.c_str(),
"0", // target size
patch.c_str(),
};
ASSERT_NE(0, applypatch_modes(args6.size(), args6.data()));
TEST_F(ApplyPatchModesTest, CheckMode) {
ASSERT_EQ(0, InvokeApplyPatchModes({ "applypatch", "--check", recovery }));
ASSERT_EQ(0, InvokeApplyPatchModes({ "applypatch", "--check", source }));
}
TEST_F(ApplyPatchModesTest, CheckModeInvalidArgs) {
// Insufficient args.
ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-c" }));
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, applypatch_modes(2, (const char* []){ "applypatch", "-l" }));
ASSERT_EQ(0, InvokeApplyPatchModes({ "applypatch", "--license" }));
}