Merge "updater: add functions to modify dynamic partition metadata"
This commit is contained in:
commit
27aa9404fc
7 changed files with 534 additions and 9 deletions
|
@ -26,12 +26,14 @@ cc_defaults {
|
||||||
"libedify",
|
"libedify",
|
||||||
"libotautil",
|
"libotautil",
|
||||||
"libext4_utils",
|
"libext4_utils",
|
||||||
|
"libdm",
|
||||||
"libfec",
|
"libfec",
|
||||||
"libfec_rs",
|
"libfec_rs",
|
||||||
"libverity_tree",
|
"libverity_tree",
|
||||||
"libfs_mgr",
|
"libfs_mgr",
|
||||||
"libgtest_prod",
|
"libgtest_prod",
|
||||||
"liblog",
|
"liblog",
|
||||||
|
"liblp",
|
||||||
"libselinux",
|
"libselinux",
|
||||||
"libsparse",
|
"libsparse",
|
||||||
"libsquashfs_utils",
|
"libsquashfs_utils",
|
||||||
|
@ -66,6 +68,7 @@ cc_library_static {
|
||||||
srcs: [
|
srcs: [
|
||||||
"blockimg.cpp",
|
"blockimg.cpp",
|
||||||
"commands.cpp",
|
"commands.cpp",
|
||||||
|
"dynamic_partitions.cpp",
|
||||||
"install.cpp",
|
"install.cpp",
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
@ -29,12 +29,14 @@ updater_common_static_libraries := \
|
||||||
libedify \
|
libedify \
|
||||||
libotautil \
|
libotautil \
|
||||||
libext4_utils \
|
libext4_utils \
|
||||||
|
libdm \
|
||||||
libfec \
|
libfec \
|
||||||
libfec_rs \
|
libfec_rs \
|
||||||
libverity_tree \
|
libverity_tree \
|
||||||
libfs_mgr \
|
libfs_mgr \
|
||||||
libgtest_prod \
|
libgtest_prod \
|
||||||
liblog \
|
liblog \
|
||||||
|
liblp \
|
||||||
libselinux \
|
libselinux \
|
||||||
libsparse \
|
libsparse \
|
||||||
libsquashfs_utils \
|
libsquashfs_utils \
|
||||||
|
|
|
@ -70,6 +70,7 @@
|
||||||
static constexpr size_t BLOCKSIZE = 4096;
|
static constexpr size_t BLOCKSIZE = 4096;
|
||||||
static constexpr mode_t STASH_DIRECTORY_MODE = 0700;
|
static constexpr mode_t STASH_DIRECTORY_MODE = 0700;
|
||||||
static constexpr mode_t STASH_FILE_MODE = 0600;
|
static constexpr mode_t STASH_FILE_MODE = 0600;
|
||||||
|
static constexpr mode_t MARKER_DIRECTORY_MODE = 0700;
|
||||||
|
|
||||||
static CauseCode failure_type = kNoCause;
|
static CauseCode failure_type = kNoCause;
|
||||||
static bool is_retry = false;
|
static bool is_retry = false;
|
||||||
|
@ -167,15 +168,22 @@ static bool UpdateLastCommandIndex(size_t command_index, const std::string& comm
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool SetPartitionUpdatedMarker(const std::string& marker) {
|
bool SetUpdatedMarker(const std::string& marker) {
|
||||||
|
auto dirname = android::base::Dirname(marker);
|
||||||
|
auto res = mkdir(dirname.c_str(), MARKER_DIRECTORY_MODE);
|
||||||
|
if (res == -1 && errno != EEXIST) {
|
||||||
|
PLOG(ERROR) << "Failed to create directory for marker: " << dirname;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!android::base::WriteStringToFile("", marker)) {
|
if (!android::base::WriteStringToFile("", marker)) {
|
||||||
PLOG(ERROR) << "Failed to write to marker file " << marker;
|
PLOG(ERROR) << "Failed to write to marker file " << marker;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!FsyncDir(android::base::Dirname(marker))) {
|
if (!FsyncDir(dirname)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LOG(INFO) << "Wrote partition updated marker to " << marker;
|
LOG(INFO) << "Wrote updated marker to " << marker;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1573,6 +1581,43 @@ using CommandFunction = std::function<int(CommandParameters&)>;
|
||||||
|
|
||||||
using CommandMap = std::unordered_map<Command::Type, CommandFunction>;
|
using CommandMap = std::unordered_map<Command::Type, CommandFunction>;
|
||||||
|
|
||||||
|
static bool Sha1DevicePath(const std::string& path, uint8_t digest[SHA_DIGEST_LENGTH]) {
|
||||||
|
auto device_name = android::base::Basename(path);
|
||||||
|
auto dm_target_name_path = "/sys/block/" + device_name + "/dm/name";
|
||||||
|
|
||||||
|
struct stat sb;
|
||||||
|
if (stat(dm_target_name_path.c_str(), &sb) == 0) {
|
||||||
|
// This is a device mapper target. Use partition name as part of the hash instead. Do not
|
||||||
|
// include extents as part of the hash, because the size of a partition may be shrunk after
|
||||||
|
// the patches are applied.
|
||||||
|
std::string dm_target_name;
|
||||||
|
if (!android::base::ReadFileToString(dm_target_name_path, &dm_target_name)) {
|
||||||
|
PLOG(ERROR) << "Cannot read " << dm_target_name_path;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SHA1(reinterpret_cast<const uint8_t*>(dm_target_name.data()), dm_target_name.size(), digest);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errno != ENOENT) {
|
||||||
|
// This is a device mapper target, but its name cannot be retrieved.
|
||||||
|
PLOG(ERROR) << "Cannot get dm target name for " << path;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This doesn't appear to be a device mapper target, but if its name starts with dm-, something
|
||||||
|
// else might have gone wrong.
|
||||||
|
if (android::base::StartsWith(device_name, "dm-")) {
|
||||||
|
LOG(WARNING) << "Device " << path << " starts with dm- but is not mapped by device-mapper.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stash directory should be different for each partition to avoid conflicts when updating
|
||||||
|
// multiple partitions at the same time, so we use the hash of the block device name as the base
|
||||||
|
// directory.
|
||||||
|
SHA1(reinterpret_cast<const uint8_t*>(path.data()), path.size(), digest);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static Value* PerformBlockImageUpdate(const char* name, State* state,
|
static Value* PerformBlockImageUpdate(const char* name, State* state,
|
||||||
const std::vector<std::unique_ptr<Expr>>& argv,
|
const std::vector<std::unique_ptr<Expr>>& argv,
|
||||||
const CommandMap& command_map, bool dryrun) {
|
const CommandMap& command_map, bool dryrun) {
|
||||||
|
@ -1657,12 +1702,10 @@ static Value* PerformBlockImageUpdate(const char* name, State* state,
|
||||||
return StringValue("");
|
return StringValue("");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stash directory should be different for each partition to avoid conflicts when updating
|
|
||||||
// multiple partitions at the same time, so we use the hash of the block device name as the base
|
|
||||||
// directory.
|
|
||||||
uint8_t digest[SHA_DIGEST_LENGTH];
|
uint8_t digest[SHA_DIGEST_LENGTH];
|
||||||
SHA1(reinterpret_cast<const uint8_t*>(blockdev_filename->data.data()),
|
if (!Sha1DevicePath(blockdev_filename->data, digest)) {
|
||||||
blockdev_filename->data.size(), digest);
|
return StringValue("");
|
||||||
|
}
|
||||||
params.stashbase = print_sha1(digest);
|
params.stashbase = print_sha1(digest);
|
||||||
|
|
||||||
// Possibly do return early on retry, by checking the marker. If the update on this partition has
|
// Possibly do return early on retry, by checking the marker. If the update on this partition has
|
||||||
|
@ -1884,7 +1927,7 @@ pbiudone:
|
||||||
// Create a marker on /cache partition, which allows skipping the update on this partition on
|
// Create a marker on /cache partition, which allows skipping the update on this partition on
|
||||||
// retry. The marker will be removed once booting into normal boot, or before starting next
|
// retry. The marker will be removed once booting into normal boot, or before starting next
|
||||||
// fresh install.
|
// fresh install.
|
||||||
if (!SetPartitionUpdatedMarker(updated_marker)) {
|
if (!SetUpdatedMarker(updated_marker)) {
|
||||||
LOG(WARNING) << "Failed to set updated marker; continuing";
|
LOG(WARNING) << "Failed to set updated marker; continuing";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
435
updater/dynamic_partitions.cpp
Normal file
435
updater/dynamic_partitions.cpp
Normal file
|
@ -0,0 +1,435 @@
|
||||||
|
/*
|
||||||
|
* 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/dynamic_partitions.h"
|
||||||
|
|
||||||
|
#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 "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) {
|
||||||
|
if (argv.size() != arg_names.size()) {
|
||||||
|
ErrorAbort(state, kArgsParsingFailure, "%s expects %zu arguments, got %zu", name,
|
||||||
|
arg_names.size(), argv.size());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<Value>> args;
|
||||||
|
if (!ReadValueArgs(state, argv, &args)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK_EQ(args.size(), arg_names.size());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < arg_names.size(); ++i) {
|
||||||
|
if (args[i]->type != Value::Type::STRING) {
|
||||||
|
ErrorAbort(state, kArgsParsingFailure, "%s argument to %s must be string",
|
||||||
|
arg_names[i].c_str(), name);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> ret;
|
||||||
|
std::transform(args.begin(), args.end(), std::back_inserter(ret),
|
||||||
|
[](const auto& arg) { return arg->data; });
|
||||||
|
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("");
|
||||||
|
}
|
||||||
|
|
||||||
|
Value* MapPartitionFn(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("");
|
||||||
|
|
||||||
|
std::string path;
|
||||||
|
bool result = 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
|
||||||
|
|
||||||
|
Value* UpdateDynamicPartitionsFn(const char* name, State* state,
|
||||||
|
const std::vector<std::unique_ptr<Expr>>& argv) {
|
||||||
|
if (argv.size() != 1) {
|
||||||
|
ErrorAbort(state, kArgsParsingFailure, "%s expects 1 arguments, got %zu", name, argv.size());
|
||||||
|
return StringValue("");
|
||||||
|
}
|
||||||
|
std::vector<std::unique_ptr<Value>> args;
|
||||||
|
if (!ReadValueArgs(state, argv, &args)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const std::unique_ptr<Value>& op_list_value = args[0];
|
||||||
|
if (op_list_value->type != Value::Type::BLOB) {
|
||||||
|
ErrorAbort(state, kArgsParsingFailure, "op_list argument to %s must be blob", name);
|
||||||
|
return StringValue("");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string updated_marker = Paths::Get().stash_directory_base() + kMetadataUpdatedMarker;
|
||||||
|
if (state->is_retry) {
|
||||||
|
struct stat sb;
|
||||||
|
int result = stat(updated_marker.c_str(), &sb);
|
||||||
|
if (result == 0) {
|
||||||
|
LOG(INFO) << "Skipping already updated dynamic partition metadata based on marker";
|
||||||
|
return StringValue("t");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Delete the obsolete marker if any.
|
||||||
|
std::string err;
|
||||||
|
if (!android::base::RemoveFileIfExists(updated_marker, &err)) {
|
||||||
|
LOG(ERROR) << "Failed to remove dynamic partition metadata updated marker " << updated_marker
|
||||||
|
<< ": " << err;
|
||||||
|
return StringValue("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.";
|
||||||
|
return StringValue("");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetUpdatedMarker(updated_marker)) {
|
||||||
|
LOG(ERROR) << "Failed to set metadata updated marker.";
|
||||||
|
return StringValue("");
|
||||||
|
}
|
||||||
|
|
||||||
|
return StringValue("t");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterDynamicPartitionsFunctions() {
|
||||||
|
RegisterFunction("unmap_partition", UnmapPartitionFn);
|
||||||
|
RegisterFunction("map_partition", MapPartitionFn);
|
||||||
|
RegisterFunction("update_dynamic_partitions", UpdateDynamicPartitionsFn);
|
||||||
|
}
|
21
updater/include/private/utils.h
Normal file
21
updater/include/private/utils.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
bool SetUpdatedMarker(const std::string& marker);
|
19
updater/include/updater/dynamic_partitions.h
Normal file
19
updater/include/updater/dynamic_partitions.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
void RegisterDynamicPartitionsFunctions();
|
|
@ -35,6 +35,7 @@
|
||||||
#include "otautil/error_code.h"
|
#include "otautil/error_code.h"
|
||||||
#include "otautil/sysutil.h"
|
#include "otautil/sysutil.h"
|
||||||
#include "updater/blockimg.h"
|
#include "updater/blockimg.h"
|
||||||
|
#include "updater/dynamic_partitions.h"
|
||||||
#include "updater/install.h"
|
#include "updater/install.h"
|
||||||
|
|
||||||
// Generated by the makefile, this function defines the
|
// Generated by the makefile, this function defines the
|
||||||
|
@ -125,6 +126,7 @@ int main(int argc, char** argv) {
|
||||||
RegisterBuiltins();
|
RegisterBuiltins();
|
||||||
RegisterInstallFunctions();
|
RegisterInstallFunctions();
|
||||||
RegisterBlockImageFunctions();
|
RegisterBlockImageFunctions();
|
||||||
|
RegisterDynamicPartitionsFunctions();
|
||||||
RegisterDeviceExtensions();
|
RegisterDeviceExtensions();
|
||||||
|
|
||||||
// Parse the script.
|
// Parse the script.
|
||||||
|
|
Loading…
Reference in a new issue