diff options
-rw-r--r-- | edify/include/edify/updater_runtime_interface.h | 7 | ||||
-rw-r--r-- | updater/Android.bp | 2 | ||||
-rw-r--r-- | updater/dynamic_partitions.cpp | 313 | ||||
-rw-r--r-- | updater/include/updater/simulator_runtime.h | 4 | ||||
-rw-r--r-- | updater/include/updater/updater_runtime.h | 5 | ||||
-rw-r--r-- | updater/simulator_runtime.cpp | 31 | ||||
-rw-r--r-- | updater/update_simulator_main.cpp | 1 | ||||
-rw-r--r-- | updater/updater_runtime_dynamic_partitions.cpp | 336 |
8 files changed, 394 insertions, 305 deletions
diff --git a/edify/include/edify/updater_runtime_interface.h b/edify/include/edify/updater_runtime_interface.h index 15ccd832d..d3d26da64 100644 --- a/edify/include/edify/updater_runtime_interface.h +++ b/edify/include/edify/updater_runtime_interface.h @@ -66,4 +66,9 @@ class UpdaterRuntimeInterface { // Runs tune2fs with arguments |args|. virtual int Tune2Fs(const std::vector<std::string>& args) const = 0; -};
\ No newline at end of file + + // 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; +}; diff --git a/updater/Android.bp b/updater/Android.bp index 93eeece51..063366e5e 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -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", ], diff --git a/updater/dynamic_partitions.cpp b/updater/dynamic_partitions.cpp index b50dd75f9..a340116fe 100644 --- a/updater/dynamic_partitions.cpp +++ b/updater/dynamic_partitions.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(""); } diff --git a/updater/include/updater/simulator_runtime.h b/updater/include/updater/simulator_runtime.h index 629095886..9f7847b4f 100644 --- a/updater/include/updater/simulator_runtime.h +++ b/updater/include/updater/simulator_runtime.h @@ -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; diff --git a/updater/include/updater/updater_runtime.h b/updater/include/updater/updater_runtime.h index e97eb49b1..8fc066f6a 100644 --- a/updater/include/updater/updater_runtime.h +++ b/updater/include/updater/updater_runtime.h @@ -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 }; }; diff --git a/updater/simulator_runtime.cpp b/updater/simulator_runtime.cpp index c3b3d951c..c8718c71a 100644 --- a/updater/simulator_runtime.cpp +++ b/updater/simulator_runtime.cpp @@ -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; +} diff --git a/updater/update_simulator_main.cpp b/updater/update_simulator_main.cpp index 94924e783..019c404ef 100644 --- a/updater/update_simulator_main.cpp +++ b/updater/update_simulator_main.cpp @@ -48,6 +48,7 @@ int main(int argc, char** argv) { RegisterBuiltins(); RegisterInstallFunctions(); RegisterBlockImageFunctions(); + RegisterDynamicPartitionsFunctions(); TemporaryFile temp_saved_source; TemporaryFile temp_last_command; diff --git a/updater/updater_runtime_dynamic_partitions.cpp b/updater/updater_runtime_dynamic_partitions.cpp new file mode 100644 index 000000000..e9f4c97c9 --- /dev/null +++ b/updater/updater_runtime_dynamic_partitions.cpp @@ -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; +} |