Implement updater runtime for dynamic partitions
The simulator skips executing the operations for dynamic partitions, and will use the logical images under target_files/IMAGES directly. (Similar to the targets without DAP enabled) Bug: 131911365 Test: run update on cuttlefish, run simulator Change-Id: Id318d97ece4560df9f20dc5cabeb8b2e261bdf9c
This commit is contained in:
parent
090b62832f
commit
d118833f3e
8 changed files with 394 additions and 305 deletions
|
@ -66,4 +66,9 @@ class UpdaterRuntimeInterface {
|
|||
|
||||
// Runs tune2fs with arguments |args|.
|
||||
virtual int Tune2Fs(const std::vector<std::string>& args) const = 0;
|
||||
};
|
||||
|
||||
// Dynamic partition related functions.
|
||||
virtual bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) = 0;
|
||||
virtual bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) = 0;
|
||||
virtual bool UpdateDynamicPartitions(const std::string_view op_list_value) = 0;
|
||||
};
|
||||
|
|
|
@ -104,6 +104,7 @@ cc_library_static {
|
|||
srcs: [
|
||||
"dynamic_partitions.cpp",
|
||||
"updater_runtime.cpp",
|
||||
"updater_runtime_dynamic_partitions.cpp",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
|
@ -129,6 +130,7 @@ cc_library_host_static {
|
|||
|
||||
srcs: [
|
||||
"build_info.cpp",
|
||||
"dynamic_partitions.cpp",
|
||||
"simulator_runtime.cpp",
|
||||
"target_files.cpp",
|
||||
],
|
||||
|
|
|
@ -19,46 +19,20 @@
|
|||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <fs_mgr.h>
|
||||
#include <fs_mgr_dm_linear.h>
|
||||
#include <libdm/dm.h>
|
||||
#include <liblp/builder.h>
|
||||
|
||||
#include "edify/expr.h"
|
||||
#include "edify/updater_runtime_interface.h"
|
||||
#include "otautil/error_code.h"
|
||||
#include "otautil/paths.h"
|
||||
#include "private/utils.h"
|
||||
|
||||
using android::base::ParseUint;
|
||||
using android::dm::DeviceMapper;
|
||||
using android::dm::DmDeviceState;
|
||||
using android::fs_mgr::CreateLogicalPartition;
|
||||
using android::fs_mgr::DestroyLogicalPartition;
|
||||
using android::fs_mgr::LpMetadata;
|
||||
using android::fs_mgr::MetadataBuilder;
|
||||
using android::fs_mgr::Partition;
|
||||
using android::fs_mgr::PartitionOpener;
|
||||
|
||||
static constexpr std::chrono::milliseconds kMapTimeout{ 1000 };
|
||||
static constexpr char kMetadataUpdatedMarker[] = "/dynamic_partition_metadata.UPDATED";
|
||||
|
||||
static std::string GetSuperDevice() {
|
||||
return "/dev/block/by-name/" + fs_mgr_get_super_partition_name();
|
||||
}
|
||||
|
||||
static std::vector<std::string> ReadStringArgs(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv,
|
||||
const std::vector<std::string>& arg_names) {
|
||||
|
@ -89,40 +63,14 @@ static std::vector<std::string> ReadStringArgs(const char* name, State* state,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
|
||||
auto state = DeviceMapper::Instance().GetState(partition_name);
|
||||
if (state == DmDeviceState::INVALID) {
|
||||
return true;
|
||||
}
|
||||
if (state == DmDeviceState::ACTIVE) {
|
||||
return DestroyLogicalPartition(partition_name, kMapTimeout);
|
||||
}
|
||||
LOG(ERROR) << "Unknown device mapper state: "
|
||||
<< static_cast<std::underlying_type_t<DmDeviceState>>(state);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) {
|
||||
auto state = DeviceMapper::Instance().GetState(partition_name);
|
||||
if (state == DmDeviceState::INVALID) {
|
||||
return CreateLogicalPartition(GetSuperDevice(), 0 /* metadata slot */, partition_name,
|
||||
true /* force writable */, kMapTimeout, path);
|
||||
}
|
||||
|
||||
if (state == DmDeviceState::ACTIVE) {
|
||||
return DeviceMapper::Instance().GetDmDevicePathByName(partition_name, path);
|
||||
}
|
||||
LOG(ERROR) << "Unknown device mapper state: "
|
||||
<< static_cast<std::underlying_type_t<DmDeviceState>>(state);
|
||||
return false;
|
||||
}
|
||||
|
||||
Value* UnmapPartitionFn(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
auto args = ReadStringArgs(name, state, argv, { "name" });
|
||||
if (args.empty()) return StringValue("");
|
||||
|
||||
return UnmapPartitionOnDeviceMapper(args[0]) ? StringValue("t") : StringValue("");
|
||||
auto updater_runtime = state->updater->GetRuntime();
|
||||
return updater_runtime->UnmapPartitionOnDeviceMapper(args[0]) ? StringValue("t")
|
||||
: StringValue("");
|
||||
}
|
||||
|
||||
Value* MapPartitionFn(const char* name, State* state,
|
||||
|
@ -131,207 +79,12 @@ Value* MapPartitionFn(const char* name, State* state,
|
|||
if (args.empty()) return StringValue("");
|
||||
|
||||
std::string path;
|
||||
bool result = MapPartitionOnDeviceMapper(args[0], &path);
|
||||
auto updater_runtime = state->updater->GetRuntime();
|
||||
bool result = updater_runtime->MapPartitionOnDeviceMapper(args[0], &path);
|
||||
return result ? StringValue(path) : StringValue("");
|
||||
}
|
||||
|
||||
namespace { // Ops
|
||||
|
||||
struct OpParameters {
|
||||
std::vector<std::string> tokens;
|
||||
MetadataBuilder* builder;
|
||||
|
||||
bool ExpectArgSize(size_t size) const {
|
||||
CHECK(!tokens.empty());
|
||||
auto actual = tokens.size() - 1;
|
||||
if (actual != size) {
|
||||
LOG(ERROR) << "Op " << op() << " expects " << size << " args, got " << actual;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const std::string& op() const {
|
||||
CHECK(!tokens.empty());
|
||||
return tokens[0];
|
||||
}
|
||||
const std::string& arg(size_t pos) const {
|
||||
CHECK_LE(pos + 1, tokens.size());
|
||||
return tokens[pos + 1];
|
||||
}
|
||||
std::optional<uint64_t> uint_arg(size_t pos, const std::string& name) const {
|
||||
auto str = arg(pos);
|
||||
uint64_t ret;
|
||||
if (!ParseUint(str, &ret)) {
|
||||
LOG(ERROR) << "Op " << op() << " expects uint64 for argument " << name << ", got " << str;
|
||||
return std::nullopt;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
using OpFunction = std::function<bool(const OpParameters&)>;
|
||||
using OpMap = std::map<std::string, OpFunction>;
|
||||
|
||||
bool PerformOpResize(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(2)) return false;
|
||||
const auto& partition_name = params.arg(0);
|
||||
auto size = params.uint_arg(1, "size");
|
||||
if (!size.has_value()) return false;
|
||||
|
||||
auto partition = params.builder->FindPartition(partition_name);
|
||||
if (partition == nullptr) {
|
||||
LOG(ERROR) << "Failed to find partition " << partition_name
|
||||
<< " in dynamic partition metadata.";
|
||||
return false;
|
||||
}
|
||||
if (!UnmapPartitionOnDeviceMapper(partition_name)) {
|
||||
LOG(ERROR) << "Cannot unmap " << partition_name << " before resizing.";
|
||||
return false;
|
||||
}
|
||||
if (!params.builder->ResizePartition(partition, size.value())) {
|
||||
LOG(ERROR) << "Failed to resize partition " << partition_name << " to size " << *size << ".";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformOpRemove(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(1)) return false;
|
||||
const auto& partition_name = params.arg(0);
|
||||
|
||||
if (!UnmapPartitionOnDeviceMapper(partition_name)) {
|
||||
LOG(ERROR) << "Cannot unmap " << partition_name << " before removing.";
|
||||
return false;
|
||||
}
|
||||
params.builder->RemovePartition(partition_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformOpAdd(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(2)) return false;
|
||||
const auto& partition_name = params.arg(0);
|
||||
const auto& group_name = params.arg(1);
|
||||
|
||||
if (params.builder->AddPartition(partition_name, group_name, LP_PARTITION_ATTR_READONLY) ==
|
||||
nullptr) {
|
||||
LOG(ERROR) << "Failed to add partition " << partition_name << " to group " << group_name << ".";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformOpMove(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(2)) return false;
|
||||
const auto& partition_name = params.arg(0);
|
||||
const auto& new_group = params.arg(1);
|
||||
|
||||
auto partition = params.builder->FindPartition(partition_name);
|
||||
if (partition == nullptr) {
|
||||
LOG(ERROR) << "Cannot move partition " << partition_name << " to group " << new_group
|
||||
<< " because it is not found.";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto old_group = partition->group_name();
|
||||
if (old_group != new_group) {
|
||||
if (!params.builder->ChangePartitionGroup(partition, new_group)) {
|
||||
LOG(ERROR) << "Cannot move partition " << partition_name << " from group " << old_group
|
||||
<< " to group " << new_group << ".";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformOpAddGroup(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(2)) return false;
|
||||
const auto& group_name = params.arg(0);
|
||||
auto maximum_size = params.uint_arg(1, "maximum_size");
|
||||
if (!maximum_size.has_value()) return false;
|
||||
|
||||
auto group = params.builder->FindGroup(group_name);
|
||||
if (group != nullptr) {
|
||||
LOG(ERROR) << "Cannot add group " << group_name << " because it already exists.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maximum_size.value() == 0) {
|
||||
LOG(WARNING) << "Adding group " << group_name << " with no size limits.";
|
||||
}
|
||||
|
||||
if (!params.builder->AddGroup(group_name, maximum_size.value())) {
|
||||
LOG(ERROR) << "Failed to add group " << group_name << " with maximum size "
|
||||
<< maximum_size.value() << ".";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformOpResizeGroup(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(2)) return false;
|
||||
const auto& group_name = params.arg(0);
|
||||
auto new_size = params.uint_arg(1, "maximum_size");
|
||||
if (!new_size.has_value()) return false;
|
||||
|
||||
auto group = params.builder->FindGroup(group_name);
|
||||
if (group == nullptr) {
|
||||
LOG(ERROR) << "Cannot resize group " << group_name << " because it is not found.";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto old_size = group->maximum_size();
|
||||
if (old_size != new_size.value()) {
|
||||
if (!params.builder->ChangeGroupSize(group_name, new_size.value())) {
|
||||
LOG(ERROR) << "Cannot resize group " << group_name << " from " << old_size << " to "
|
||||
<< new_size.value() << ".";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> ListPartitionNamesInGroup(MetadataBuilder* builder,
|
||||
const std::string& group_name) {
|
||||
auto partitions = builder->ListPartitionsInGroup(group_name);
|
||||
std::vector<std::string> partition_names;
|
||||
std::transform(partitions.begin(), partitions.end(), std::back_inserter(partition_names),
|
||||
[](Partition* partition) { return partition->name(); });
|
||||
return partition_names;
|
||||
}
|
||||
|
||||
bool PerformOpRemoveGroup(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(1)) return false;
|
||||
const auto& group_name = params.arg(0);
|
||||
|
||||
auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
|
||||
if (!partition_names.empty()) {
|
||||
LOG(ERROR) << "Cannot remove group " << group_name << " because it still contains partitions ["
|
||||
<< android::base::Join(partition_names, ", ") << "]";
|
||||
return false;
|
||||
}
|
||||
params.builder->RemoveGroupAndPartitions(group_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformOpRemoveAllGroups(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(0)) return false;
|
||||
|
||||
auto group_names = params.builder->ListGroups();
|
||||
for (const auto& group_name : group_names) {
|
||||
auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
|
||||
for (const auto& partition_name : partition_names) {
|
||||
if (!UnmapPartitionOnDeviceMapper(partition_name)) {
|
||||
LOG(ERROR) << "Cannot unmap " << partition_name << " before removing group " << group_name
|
||||
<< ".";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
params.builder->RemoveGroupAndPartitions(group_name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
static constexpr char kMetadataUpdatedMarker[] = "/dynamic_partition_metadata.UPDATED";
|
||||
|
||||
Value* UpdateDynamicPartitionsFn(const char* name, State* state,
|
||||
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||
|
@ -367,56 +120,8 @@ Value* UpdateDynamicPartitionsFn(const char* name, State* state,
|
|||
}
|
||||
}
|
||||
|
||||
auto super_device = GetSuperDevice();
|
||||
auto builder = MetadataBuilder::New(PartitionOpener(), super_device, 0);
|
||||
if (builder == nullptr) {
|
||||
LOG(ERROR) << "Failed to load dynamic partition metadata.";
|
||||
return StringValue("");
|
||||
}
|
||||
|
||||
static const OpMap op_map{
|
||||
// clang-format off
|
||||
{"resize", PerformOpResize},
|
||||
{"remove", PerformOpRemove},
|
||||
{"add", PerformOpAdd},
|
||||
{"move", PerformOpMove},
|
||||
{"add_group", PerformOpAddGroup},
|
||||
{"resize_group", PerformOpResizeGroup},
|
||||
{"remove_group", PerformOpRemoveGroup},
|
||||
{"remove_all_groups", PerformOpRemoveAllGroups},
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
std::vector<std::string> lines = android::base::Split(op_list_value->data, "\n");
|
||||
for (const auto& line : lines) {
|
||||
auto comment_idx = line.find('#');
|
||||
auto op_and_args = comment_idx == std::string::npos ? line : line.substr(0, comment_idx);
|
||||
op_and_args = android::base::Trim(op_and_args);
|
||||
if (op_and_args.empty()) continue;
|
||||
|
||||
auto tokens = android::base::Split(op_and_args, " ");
|
||||
const auto& op = tokens[0];
|
||||
auto it = op_map.find(op);
|
||||
if (it == op_map.end()) {
|
||||
LOG(ERROR) << "Unknown operation in op_list: " << op;
|
||||
return StringValue("");
|
||||
}
|
||||
OpParameters params;
|
||||
params.tokens = tokens;
|
||||
params.builder = builder.get();
|
||||
if (!it->second(params)) {
|
||||
return StringValue("");
|
||||
}
|
||||
}
|
||||
|
||||
auto metadata = builder->Export();
|
||||
if (metadata == nullptr) {
|
||||
LOG(ERROR) << "Failed to export metadata.";
|
||||
return StringValue("");
|
||||
}
|
||||
|
||||
if (!UpdatePartitionTable(super_device, *metadata, 0)) {
|
||||
LOG(ERROR) << "Failed to write metadata.";
|
||||
auto updater_runtime = state->updater->GetRuntime();
|
||||
if (!updater_runtime->UpdateDynamicPartitions(op_list_value->data)) {
|
||||
return StringValue("");
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,10 @@ class SimulatorRuntime : public UpdaterRuntimeInterface {
|
|||
int RunProgram(const std::vector<std::string>& args, bool is_vfork) const override;
|
||||
int Tune2Fs(const std::vector<std::string>& args) const override;
|
||||
|
||||
bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) override;
|
||||
bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) override;
|
||||
bool UpdateDynamicPartitions(const std::string_view op_list_value) override;
|
||||
|
||||
private:
|
||||
std::string FindBlockDeviceName(const std::string_view name) const override;
|
||||
|
||||
|
|
|
@ -53,5 +53,10 @@ class UpdaterRuntime : public UpdaterRuntimeInterface {
|
|||
int RunProgram(const std::vector<std::string>& args, bool is_vfork) const override;
|
||||
int Tune2Fs(const std::vector<std::string>& args) const override;
|
||||
|
||||
bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) override;
|
||||
bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) override;
|
||||
bool UpdateDynamicPartitions(const std::string_view op_list_value) override;
|
||||
|
||||
private:
|
||||
struct selabel_handle* sehandle_{ nullptr };
|
||||
};
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
|
@ -95,3 +97,32 @@ bool SimulatorRuntime::WriteStringToFile(const std::string_view content,
|
|||
LOG(INFO) << "SKip writing " << content.size() << " bytes to file " << filename;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SimulatorRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name,
|
||||
std::string* path) {
|
||||
*path = partition_name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SimulatorRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
|
||||
LOG(INFO) << "Skip unmapping " << partition_name;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SimulatorRuntime::UpdateDynamicPartitions(const std::string_view op_list_value) {
|
||||
const std::unordered_set<std::string> commands{
|
||||
"resize", "remove", "add", "move",
|
||||
"add_group", "resize_group", "remove_group", "remove_all_groups",
|
||||
};
|
||||
|
||||
std::vector<std::string> lines = android::base::Split(std::string(op_list_value), "\n");
|
||||
for (const auto& line : lines) {
|
||||
if (line.empty() || line[0] == '#') continue;
|
||||
auto tokens = android::base::Split(line, " ");
|
||||
if (commands.find(tokens[0]) == commands.end()) {
|
||||
LOG(ERROR) << "Unknown operation in op_list: " << line;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ int main(int argc, char** argv) {
|
|||
RegisterBuiltins();
|
||||
RegisterInstallFunctions();
|
||||
RegisterBlockImageFunctions();
|
||||
RegisterDynamicPartitionsFunctions();
|
||||
|
||||
TemporaryFile temp_saved_source;
|
||||
TemporaryFile temp_last_command;
|
||||
|
|
336
updater/updater_runtime_dynamic_partitions.cpp
Normal file
336
updater/updater_runtime_dynamic_partitions.cpp
Normal file
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 "updater/updater_runtime.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <fs_mgr.h>
|
||||
#include <fs_mgr_dm_linear.h>
|
||||
#include <libdm/dm.h>
|
||||
#include <liblp/builder.h>
|
||||
|
||||
using android::dm::DeviceMapper;
|
||||
using android::dm::DmDeviceState;
|
||||
using android::fs_mgr::CreateLogicalPartition;
|
||||
using android::fs_mgr::DestroyLogicalPartition;
|
||||
using android::fs_mgr::LpMetadata;
|
||||
using android::fs_mgr::MetadataBuilder;
|
||||
using android::fs_mgr::Partition;
|
||||
using android::fs_mgr::PartitionOpener;
|
||||
|
||||
static constexpr std::chrono::milliseconds kMapTimeout{ 1000 };
|
||||
|
||||
static std::string GetSuperDevice() {
|
||||
return "/dev/block/by-name/" + fs_mgr_get_super_partition_name();
|
||||
}
|
||||
|
||||
static bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
|
||||
auto state = DeviceMapper::Instance().GetState(partition_name);
|
||||
if (state == DmDeviceState::INVALID) {
|
||||
return true;
|
||||
}
|
||||
if (state == DmDeviceState::ACTIVE) {
|
||||
return DestroyLogicalPartition(partition_name, kMapTimeout);
|
||||
}
|
||||
LOG(ERROR) << "Unknown device mapper state: "
|
||||
<< static_cast<std::underlying_type_t<DmDeviceState>>(state);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UpdaterRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name,
|
||||
std::string* path) {
|
||||
auto state = DeviceMapper::Instance().GetState(partition_name);
|
||||
if (state == DmDeviceState::INVALID) {
|
||||
return CreateLogicalPartition(GetSuperDevice(), 0 /* metadata slot */, partition_name,
|
||||
true /* force writable */, kMapTimeout, path);
|
||||
}
|
||||
|
||||
if (state == DmDeviceState::ACTIVE) {
|
||||
return DeviceMapper::Instance().GetDmDevicePathByName(partition_name, path);
|
||||
}
|
||||
LOG(ERROR) << "Unknown device mapper state: "
|
||||
<< static_cast<std::underlying_type_t<DmDeviceState>>(state);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UpdaterRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
|
||||
return ::UnmapPartitionOnDeviceMapper(partition_name);
|
||||
}
|
||||
|
||||
namespace { // Ops
|
||||
|
||||
struct OpParameters {
|
||||
std::vector<std::string> tokens;
|
||||
MetadataBuilder* builder;
|
||||
|
||||
bool ExpectArgSize(size_t size) const {
|
||||
CHECK(!tokens.empty());
|
||||
auto actual = tokens.size() - 1;
|
||||
if (actual != size) {
|
||||
LOG(ERROR) << "Op " << op() << " expects " << size << " args, got " << actual;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const std::string& op() const {
|
||||
CHECK(!tokens.empty());
|
||||
return tokens[0];
|
||||
}
|
||||
const std::string& arg(size_t pos) const {
|
||||
CHECK_LE(pos + 1, tokens.size());
|
||||
return tokens[pos + 1];
|
||||
}
|
||||
std::optional<uint64_t> uint_arg(size_t pos, const std::string& name) const {
|
||||
auto str = arg(pos);
|
||||
uint64_t ret;
|
||||
if (!android::base::ParseUint(str, &ret)) {
|
||||
LOG(ERROR) << "Op " << op() << " expects uint64 for argument " << name << ", got " << str;
|
||||
return std::nullopt;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
using OpFunction = std::function<bool(const OpParameters&)>;
|
||||
using OpMap = std::map<std::string, OpFunction>;
|
||||
|
||||
bool PerformOpResize(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(2)) return false;
|
||||
const auto& partition_name = params.arg(0);
|
||||
auto size = params.uint_arg(1, "size");
|
||||
if (!size.has_value()) return false;
|
||||
|
||||
auto partition = params.builder->FindPartition(partition_name);
|
||||
if (partition == nullptr) {
|
||||
LOG(ERROR) << "Failed to find partition " << partition_name
|
||||
<< " in dynamic partition metadata.";
|
||||
return false;
|
||||
}
|
||||
if (!UnmapPartitionOnDeviceMapper(partition_name)) {
|
||||
LOG(ERROR) << "Cannot unmap " << partition_name << " before resizing.";
|
||||
return false;
|
||||
}
|
||||
if (!params.builder->ResizePartition(partition, size.value())) {
|
||||
LOG(ERROR) << "Failed to resize partition " << partition_name << " to size " << *size << ".";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformOpRemove(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(1)) return false;
|
||||
const auto& partition_name = params.arg(0);
|
||||
|
||||
if (!UnmapPartitionOnDeviceMapper(partition_name)) {
|
||||
LOG(ERROR) << "Cannot unmap " << partition_name << " before removing.";
|
||||
return false;
|
||||
}
|
||||
params.builder->RemovePartition(partition_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformOpAdd(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(2)) return false;
|
||||
const auto& partition_name = params.arg(0);
|
||||
const auto& group_name = params.arg(1);
|
||||
|
||||
if (params.builder->AddPartition(partition_name, group_name, LP_PARTITION_ATTR_READONLY) ==
|
||||
nullptr) {
|
||||
LOG(ERROR) << "Failed to add partition " << partition_name << " to group " << group_name << ".";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformOpMove(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(2)) return false;
|
||||
const auto& partition_name = params.arg(0);
|
||||
const auto& new_group = params.arg(1);
|
||||
|
||||
auto partition = params.builder->FindPartition(partition_name);
|
||||
if (partition == nullptr) {
|
||||
LOG(ERROR) << "Cannot move partition " << partition_name << " to group " << new_group
|
||||
<< " because it is not found.";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto old_group = partition->group_name();
|
||||
if (old_group != new_group) {
|
||||
if (!params.builder->ChangePartitionGroup(partition, new_group)) {
|
||||
LOG(ERROR) << "Cannot move partition " << partition_name << " from group " << old_group
|
||||
<< " to group " << new_group << ".";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformOpAddGroup(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(2)) return false;
|
||||
const auto& group_name = params.arg(0);
|
||||
auto maximum_size = params.uint_arg(1, "maximum_size");
|
||||
if (!maximum_size.has_value()) return false;
|
||||
|
||||
auto group = params.builder->FindGroup(group_name);
|
||||
if (group != nullptr) {
|
||||
LOG(ERROR) << "Cannot add group " << group_name << " because it already exists.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maximum_size.value() == 0) {
|
||||
LOG(WARNING) << "Adding group " << group_name << " with no size limits.";
|
||||
}
|
||||
|
||||
if (!params.builder->AddGroup(group_name, maximum_size.value())) {
|
||||
LOG(ERROR) << "Failed to add group " << group_name << " with maximum size "
|
||||
<< maximum_size.value() << ".";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformOpResizeGroup(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(2)) return false;
|
||||
const auto& group_name = params.arg(0);
|
||||
auto new_size = params.uint_arg(1, "maximum_size");
|
||||
if (!new_size.has_value()) return false;
|
||||
|
||||
auto group = params.builder->FindGroup(group_name);
|
||||
if (group == nullptr) {
|
||||
LOG(ERROR) << "Cannot resize group " << group_name << " because it is not found.";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto old_size = group->maximum_size();
|
||||
if (old_size != new_size.value()) {
|
||||
if (!params.builder->ChangeGroupSize(group_name, new_size.value())) {
|
||||
LOG(ERROR) << "Cannot resize group " << group_name << " from " << old_size << " to "
|
||||
<< new_size.value() << ".";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> ListPartitionNamesInGroup(MetadataBuilder* builder,
|
||||
const std::string& group_name) {
|
||||
auto partitions = builder->ListPartitionsInGroup(group_name);
|
||||
std::vector<std::string> partition_names;
|
||||
std::transform(partitions.begin(), partitions.end(), std::back_inserter(partition_names),
|
||||
[](Partition* partition) { return partition->name(); });
|
||||
return partition_names;
|
||||
}
|
||||
|
||||
bool PerformOpRemoveGroup(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(1)) return false;
|
||||
const auto& group_name = params.arg(0);
|
||||
|
||||
auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
|
||||
if (!partition_names.empty()) {
|
||||
LOG(ERROR) << "Cannot remove group " << group_name << " because it still contains partitions ["
|
||||
<< android::base::Join(partition_names, ", ") << "]";
|
||||
return false;
|
||||
}
|
||||
params.builder->RemoveGroupAndPartitions(group_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PerformOpRemoveAllGroups(const OpParameters& params) {
|
||||
if (!params.ExpectArgSize(0)) return false;
|
||||
|
||||
auto group_names = params.builder->ListGroups();
|
||||
for (const auto& group_name : group_names) {
|
||||
auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
|
||||
for (const auto& partition_name : partition_names) {
|
||||
if (!UnmapPartitionOnDeviceMapper(partition_name)) {
|
||||
LOG(ERROR) << "Cannot unmap " << partition_name << " before removing group " << group_name
|
||||
<< ".";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
params.builder->RemoveGroupAndPartitions(group_name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool UpdaterRuntime::UpdateDynamicPartitions(const std::string_view op_list_value) {
|
||||
auto super_device = GetSuperDevice();
|
||||
auto builder = MetadataBuilder::New(PartitionOpener(), super_device, 0);
|
||||
if (builder == nullptr) {
|
||||
LOG(ERROR) << "Failed to load dynamic partition metadata.";
|
||||
return false;
|
||||
}
|
||||
|
||||
static const OpMap op_map{
|
||||
// clang-format off
|
||||
{"resize", PerformOpResize},
|
||||
{"remove", PerformOpRemove},
|
||||
{"add", PerformOpAdd},
|
||||
{"move", PerformOpMove},
|
||||
{"add_group", PerformOpAddGroup},
|
||||
{"resize_group", PerformOpResizeGroup},
|
||||
{"remove_group", PerformOpRemoveGroup},
|
||||
{"remove_all_groups", PerformOpRemoveAllGroups},
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
std::vector<std::string> lines = android::base::Split(std::string(op_list_value), "\n");
|
||||
for (const auto& line : lines) {
|
||||
auto comment_idx = line.find('#');
|
||||
auto op_and_args = comment_idx == std::string::npos ? line : line.substr(0, comment_idx);
|
||||
op_and_args = android::base::Trim(op_and_args);
|
||||
if (op_and_args.empty()) continue;
|
||||
|
||||
auto tokens = android::base::Split(op_and_args, " ");
|
||||
const auto& op = tokens[0];
|
||||
auto it = op_map.find(op);
|
||||
if (it == op_map.end()) {
|
||||
LOG(ERROR) << "Unknown operation in op_list: " << op;
|
||||
return false;
|
||||
}
|
||||
OpParameters params;
|
||||
params.tokens = tokens;
|
||||
params.builder = builder.get();
|
||||
if (!it->second(params)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto metadata = builder->Export();
|
||||
if (metadata == nullptr) {
|
||||
LOG(ERROR) << "Failed to export metadata.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!UpdatePartitionTable(super_device, *metadata, 0)) {
|
||||
LOG(ERROR) << "Failed to write metadata.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
Loading…
Reference in a new issue