summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTianjie Xu <xunchang@google.com>2019-06-24 23:46:28 +0200
committerandroid-build-merger <android-build-merger@google.com>2019-06-24 23:46:28 +0200
commitdc267f46ce397194f1cda8a24aab11c0197395a6 (patch)
tree7ded3f5d7a8e8a994209f3102c39edf0ec9ad6e5
parentMerge "updater_sample: Build SystemUpdaterSample as non-privileged app by default." am: b87520077b am: b50f855396 am: 00d4092790 (diff)
parentMerge "Implement updater runtime for dynamic partitions" am: db0a68e6b7 am: 3aaf87cf4e (diff)
downloadandroid_bootable_recovery-dc267f46ce397194f1cda8a24aab11c0197395a6.tar
android_bootable_recovery-dc267f46ce397194f1cda8a24aab11c0197395a6.tar.gz
android_bootable_recovery-dc267f46ce397194f1cda8a24aab11c0197395a6.tar.bz2
android_bootable_recovery-dc267f46ce397194f1cda8a24aab11c0197395a6.tar.lz
android_bootable_recovery-dc267f46ce397194f1cda8a24aab11c0197395a6.tar.xz
android_bootable_recovery-dc267f46ce397194f1cda8a24aab11c0197395a6.tar.zst
android_bootable_recovery-dc267f46ce397194f1cda8a24aab11c0197395a6.zip
-rw-r--r--edify/include/edify/updater_runtime_interface.h7
-rw-r--r--updater/Android.bp2
-rw-r--r--updater/dynamic_partitions.cpp313
-rw-r--r--updater/include/updater/simulator_runtime.h4
-rw-r--r--updater/include/updater/updater_runtime.h5
-rw-r--r--updater/simulator_runtime.cpp31
-rw-r--r--updater/update_simulator_main.cpp1
-rw-r--r--updater/updater_runtime_dynamic_partitions.cpp336
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;
+}