Merge "updater: Add Commmand class to manage BBOTA commands."
This commit is contained in:
@@ -37,6 +37,7 @@ LOCAL_STATIC_LIBRARIES := \
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
unit/asn1_decoder_test.cpp \
|
||||
unit/commands_test.cpp \
|
||||
unit/dirutil_test.cpp \
|
||||
unit/locale_test.cpp \
|
||||
unit/rangeset_test.cpp \
|
||||
|
||||
37
tests/unit/commands_test.cpp
Normal file
37
tests/unit/commands_test.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 <string>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "private/commands.h"
|
||||
|
||||
TEST(CommandsTest, ParseType) {
|
||||
ASSERT_EQ(Command::Type::ZERO, Command::ParseType("zero"));
|
||||
ASSERT_EQ(Command::Type::NEW, Command::ParseType("new"));
|
||||
ASSERT_EQ(Command::Type::ERASE, Command::ParseType("erase"));
|
||||
ASSERT_EQ(Command::Type::MOVE, Command::ParseType("move"));
|
||||
ASSERT_EQ(Command::Type::BSDIFF, Command::ParseType("bsdiff"));
|
||||
ASSERT_EQ(Command::Type::IMGDIFF, Command::ParseType("imgdiff"));
|
||||
ASSERT_EQ(Command::Type::STASH, Command::ParseType("stash"));
|
||||
ASSERT_EQ(Command::Type::FREE, Command::ParseType("free"));
|
||||
}
|
||||
|
||||
TEST(CommandsTest, ParseType_InvalidCommand) {
|
||||
ASSERT_EQ(Command::Type::LAST, Command::ParseType("foo"));
|
||||
ASSERT_EQ(Command::Type::LAST, Command::ParseType("bar"));
|
||||
}
|
||||
@@ -56,6 +56,7 @@ include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libupdater
|
||||
|
||||
LOCAL_SRC_FILES := \
|
||||
commands.cpp \
|
||||
install.cpp \
|
||||
blockimg.cpp
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
#include "otautil/paths.h"
|
||||
#include "otautil/print_sha1.h"
|
||||
#include "otautil/rangeset.h"
|
||||
#include "private/commands.h"
|
||||
#include "updater/install.h"
|
||||
#include "updater/updater.h"
|
||||
|
||||
@@ -546,8 +547,8 @@ static int WriteBlocks(const RangeSet& tgt, const std::vector<uint8_t>& buffer,
|
||||
struct CommandParameters {
|
||||
std::vector<std::string> tokens;
|
||||
size_t cpos;
|
||||
const char* cmdname;
|
||||
const char* cmdline;
|
||||
std::string cmdname;
|
||||
std::string cmdline;
|
||||
std::string freestash;
|
||||
std::string stashbase;
|
||||
bool canwrite;
|
||||
@@ -1496,23 +1497,13 @@ static int PerformCommandErase(CommandParameters& params) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Definitions for transfer list command functions
|
||||
typedef int (*CommandFunction)(CommandParameters&);
|
||||
using CommandFunction = std::function<int(CommandParameters&)>;
|
||||
|
||||
struct Command {
|
||||
const char* name;
|
||||
CommandFunction f;
|
||||
};
|
||||
|
||||
// args:
|
||||
// - block device (or file) to modify in-place
|
||||
// - transfer list (blob)
|
||||
// - new data stream (filename within package.zip)
|
||||
// - patch stream (filename within package.zip, must be uncompressed)
|
||||
using CommandMap = std::unordered_map<Command::Type, CommandFunction>;
|
||||
|
||||
static Value* PerformBlockImageUpdate(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
const Command* commands, size_t cmdcount, bool dryrun) {
|
||||
const CommandMap& command_map, bool dryrun) {
|
||||
CommandParameters params = {};
|
||||
params.canwrite = !dryrun;
|
||||
|
||||
@@ -1532,6 +1523,11 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// args:
|
||||
// - block device (or file) to modify in-place
|
||||
// - transfer list (blob)
|
||||
// - new data stream (filename within package.zip)
|
||||
// - patch stream (filename within package.zip, must be uncompressed)
|
||||
const std::unique_ptr<Value>& blockdev_filename = args[0];
|
||||
const std::unique_ptr<Value>& transfer_list_value = args[1];
|
||||
const std::unique_ptr<Value>& new_data_fn = args[2];
|
||||
@@ -1707,16 +1703,6 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
|
||||
skip_executed_command = false;
|
||||
}
|
||||
|
||||
// Build a map of the available commands
|
||||
std::unordered_map<std::string, const Command*> cmd_map;
|
||||
for (size_t i = 0; i < cmdcount; ++i) {
|
||||
if (cmd_map.find(commands[i].name) != cmd_map.end()) {
|
||||
LOG(ERROR) << "Error: command [" << commands[i].name << "] already exists in the cmd map.";
|
||||
return StringValue("");
|
||||
}
|
||||
cmd_map[commands[i].name] = &commands[i];
|
||||
}
|
||||
|
||||
int rc = -1;
|
||||
|
||||
static constexpr size_t kTransferListHeaderLines = 4;
|
||||
@@ -1728,36 +1714,35 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
|
||||
size_t cmdindex = i - kTransferListHeaderLines;
|
||||
params.tokens = android::base::Split(line, " ");
|
||||
params.cpos = 0;
|
||||
params.cmdname = params.tokens[params.cpos++].c_str();
|
||||
params.cmdline = line.c_str();
|
||||
params.cmdname = params.tokens[params.cpos++];
|
||||
params.cmdline = line;
|
||||
params.target_verified = false;
|
||||
|
||||
if (cmd_map.find(params.cmdname) == cmd_map.end()) {
|
||||
Command::Type cmd_type = Command::ParseType(params.cmdname);
|
||||
if (cmd_type == Command::Type::LAST) {
|
||||
LOG(ERROR) << "unexpected command [" << params.cmdname << "]";
|
||||
goto pbiudone;
|
||||
}
|
||||
|
||||
const Command* cmd = cmd_map[params.cmdname];
|
||||
const CommandFunction& performer = command_map.at(cmd_type);
|
||||
|
||||
// Skip the command if we explicitly set the corresponding function pointer to nullptr, e.g.
|
||||
// "erase" during block_image_verify.
|
||||
if (cmd->f == nullptr) {
|
||||
if (performer == nullptr) {
|
||||
LOG(DEBUG) << "skip executing command [" << line << "]";
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string cmdname = std::string(params.cmdname);
|
||||
|
||||
// Skip all commands before the saved last command index when resuming an update, except for
|
||||
// "new" command. Because new commands read in the data sequentially.
|
||||
if (params.canwrite && skip_executed_command && cmdindex <= saved_last_command_index &&
|
||||
cmdname != "new") {
|
||||
cmd_type != Command::Type::NEW) {
|
||||
LOG(INFO) << "Skipping already executed command: " << cmdindex
|
||||
<< ", last executed command for previous update: " << saved_last_command_index;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cmd->f(params) == -1) {
|
||||
if (performer(params) == -1) {
|
||||
LOG(ERROR) << "failed to execute command [" << line << "]";
|
||||
goto pbiudone;
|
||||
}
|
||||
@@ -1767,7 +1752,8 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
|
||||
// that we will resume the update from the first command in the transfer list.
|
||||
if (!params.canwrite && skip_executed_command && cmdindex <= saved_last_command_index) {
|
||||
// TODO(xunchang) check that the cmdline of the saved index is correct.
|
||||
if ((cmdname == "move" || cmdname == "bsdiff" || cmdname == "imgdiff") &&
|
||||
if ((cmd_type == Command::Type::MOVE || cmd_type == Command::Type::BSDIFF ||
|
||||
cmd_type == Command::Type::IMGDIFF) &&
|
||||
!params.target_verified) {
|
||||
LOG(WARNING) << "Previously executed command " << saved_last_command_index << ": "
|
||||
<< params.cmdline << " doesn't produce expected target blocks.";
|
||||
@@ -1775,6 +1761,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
|
||||
DeleteLastCommandFile();
|
||||
}
|
||||
}
|
||||
|
||||
if (params.canwrite) {
|
||||
if (ota_fsync(params.fd) == -1) {
|
||||
failure_type = kFsyncFailure;
|
||||
@@ -1911,38 +1898,42 @@ pbiudone:
|
||||
*/
|
||||
Value* BlockImageVerifyFn(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
// Commands which are not tested are set to nullptr to skip them completely
|
||||
const Command commands[] = {
|
||||
{ "bsdiff", PerformCommandDiff },
|
||||
{ "erase", nullptr },
|
||||
{ "free", PerformCommandFree },
|
||||
{ "imgdiff", PerformCommandDiff },
|
||||
{ "move", PerformCommandMove },
|
||||
{ "new", nullptr },
|
||||
{ "stash", PerformCommandStash },
|
||||
{ "zero", nullptr }
|
||||
};
|
||||
// Commands which are not allowed are set to nullptr to skip them completely.
|
||||
const CommandMap command_map{
|
||||
// clang-format off
|
||||
{ Command::Type::BSDIFF, PerformCommandDiff },
|
||||
{ Command::Type::ERASE, nullptr },
|
||||
{ Command::Type::FREE, PerformCommandFree },
|
||||
{ Command::Type::IMGDIFF, PerformCommandDiff },
|
||||
{ Command::Type::MOVE, PerformCommandMove },
|
||||
{ Command::Type::NEW, nullptr },
|
||||
{ Command::Type::STASH, PerformCommandStash },
|
||||
{ Command::Type::ZERO, nullptr },
|
||||
// clang-format on
|
||||
};
|
||||
CHECK_EQ(static_cast<size_t>(Command::Type::LAST), command_map.size());
|
||||
|
||||
// Perform a dry run without writing to test if an update can proceed
|
||||
return PerformBlockImageUpdate(name, state, argv, commands,
|
||||
sizeof(commands) / sizeof(commands[0]), true);
|
||||
// Perform a dry run without writing to test if an update can proceed.
|
||||
return PerformBlockImageUpdate(name, state, argv, command_map, true);
|
||||
}
|
||||
|
||||
Value* BlockImageUpdateFn(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
const Command commands[] = {
|
||||
{ "bsdiff", PerformCommandDiff },
|
||||
{ "erase", PerformCommandErase },
|
||||
{ "free", PerformCommandFree },
|
||||
{ "imgdiff", PerformCommandDiff },
|
||||
{ "move", PerformCommandMove },
|
||||
{ "new", PerformCommandNew },
|
||||
{ "stash", PerformCommandStash },
|
||||
{ "zero", PerformCommandZero }
|
||||
};
|
||||
const CommandMap command_map{
|
||||
// clang-format off
|
||||
{ Command::Type::BSDIFF, PerformCommandDiff },
|
||||
{ Command::Type::ERASE, PerformCommandErase },
|
||||
{ Command::Type::FREE, PerformCommandFree },
|
||||
{ Command::Type::IMGDIFF, PerformCommandDiff },
|
||||
{ Command::Type::MOVE, PerformCommandMove },
|
||||
{ Command::Type::NEW, PerformCommandNew },
|
||||
{ Command::Type::STASH, PerformCommandStash },
|
||||
{ Command::Type::ZERO, PerformCommandZero },
|
||||
// clang-format on
|
||||
};
|
||||
CHECK_EQ(static_cast<size_t>(Command::Type::LAST), command_map.size());
|
||||
|
||||
return PerformBlockImageUpdate(name, state, argv, commands,
|
||||
sizeof(commands) / sizeof(commands[0]), false);
|
||||
return PerformBlockImageUpdate(name, state, argv, command_map, false);
|
||||
}
|
||||
|
||||
Value* RangeSha1Fn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
|
||||
43
updater/commands.cpp
Normal file
43
updater/commands.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 "private/commands.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
Command::Type Command::ParseType(const std::string& type_str) {
|
||||
if (type_str == "zero") {
|
||||
return Type::ZERO;
|
||||
} else if (type_str == "new") {
|
||||
return Type::NEW;
|
||||
} else if (type_str == "erase") {
|
||||
return Type::ERASE;
|
||||
} else if (type_str == "move") {
|
||||
return Type::MOVE;
|
||||
} else if (type_str == "bsdiff") {
|
||||
return Type::BSDIFF;
|
||||
} else if (type_str == "imgdiff") {
|
||||
return Type::IMGDIFF;
|
||||
} else if (type_str == "stash") {
|
||||
return Type::STASH;
|
||||
} else if (type_str == "free") {
|
||||
return Type::FREE;
|
||||
}
|
||||
LOG(ERROR) << "Invalid type: " << type_str;
|
||||
return Type::LAST;
|
||||
};
|
||||
35
updater/include/private/commands.h
Normal file
35
updater/include/private/commands.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
struct Command {
|
||||
enum class Type {
|
||||
ZERO,
|
||||
NEW,
|
||||
ERASE,
|
||||
MOVE,
|
||||
BSDIFF,
|
||||
IMGDIFF,
|
||||
STASH,
|
||||
FREE,
|
||||
LAST, // Not a valid type.
|
||||
};
|
||||
|
||||
static Type ParseType(const std::string& type_str);
|
||||
};
|
||||
Reference in New Issue
Block a user