diff options
-rw-r--r-- | Android.bp | 1 | ||||
-rw-r--r-- | boot_control_android.cc | 132 | ||||
-rw-r--r-- | boot_control_android_unittest.cc | 6 | ||||
-rw-r--r-- | dynamic_partition_control_android.cc | 115 | ||||
-rw-r--r-- | dynamic_partition_control_android.h | 48 | ||||
-rw-r--r-- | dynamic_partition_control_interface.h | 26 | ||||
-rw-r--r-- | dynamic_partition_utils.cc | 39 | ||||
-rw-r--r-- | dynamic_partition_utils.h | 33 | ||||
-rw-r--r-- | mock_dynamic_partition_control.h | 5 |
9 files changed, 251 insertions, 154 deletions
@@ -214,6 +214,7 @@ cc_library_static { srcs: [ "boot_control_android.cc", "dynamic_partition_control_android.cc", + "dynamic_partition_utils.cc", ], } diff --git a/boot_control_android.cc b/boot_control_android.cc index 8ab73be1..b820deda 100644 --- a/boot_control_android.cc +++ b/boot_control_android.cc @@ -22,7 +22,6 @@ #include <base/bind.h> #include <base/logging.h> -#include <base/strings/string_util.h> #include <bootloader_message/bootloader_message.h> #include <brillo/message_loops/message_loop.h> #include <fs_mgr.h> @@ -34,7 +33,6 @@ using std::string; using android::dm::DmDeviceState; -using android::fs_mgr::Partition; using android::hardware::hidl_string; using android::hardware::Return; using android::hardware::boot::V1_0::BoolResult; @@ -112,8 +110,8 @@ bool BootControlAndroid::IsSuperBlockDevice( const string& partition_name_suffix) const { string source_device = device_dir.Append(fs_mgr_get_super_partition_name(slot)).value(); - auto source_metadata = dynamic_control_->LoadMetadataBuilder( - source_device, slot, BootControlInterface::kInvalidSlot); + auto source_metadata = + dynamic_control_->LoadMetadataBuilder(source_device, slot); return source_metadata->HasBlockDevice(partition_name_suffix); } @@ -126,8 +124,7 @@ BootControlAndroid::GetDynamicPartitionDevice( string super_device = device_dir.Append(fs_mgr_get_super_partition_name(slot)).value(); - auto builder = dynamic_control_->LoadMetadataBuilder( - super_device, slot, BootControlInterface::kInvalidSlot); + auto builder = dynamic_control_->LoadMetadataBuilder(super_device, slot); if (builder == nullptr) { LOG(ERROR) << "No metadata in slot " @@ -280,110 +277,6 @@ bool BootControlAndroid::MarkBootSuccessfulAsync( brillo::MessageLoop::kTaskIdNull; } -namespace { - -bool UpdatePartitionMetadata(DynamicPartitionControlInterface* dynamic_control, - Slot source_slot, - Slot target_slot, - const string& target_suffix, - const PartitionMetadata& partition_metadata) { - string device_dir_str; - if (!dynamic_control->GetDeviceDir(&device_dir_str)) { - return false; - } - base::FilePath device_dir(device_dir_str); - auto source_device = - device_dir.Append(fs_mgr_get_super_partition_name(source_slot)).value(); - - auto builder = dynamic_control->LoadMetadataBuilder( - source_device, source_slot, target_slot); - if (builder == nullptr) { - // TODO(elsk): allow reconstructing metadata from partition_metadata - // in recovery sideload. - LOG(ERROR) << "No metadata at " - << BootControlInterface::SlotName(source_slot); - return false; - } - - std::vector<string> groups = builder->ListGroups(); - for (const auto& group_name : groups) { - if (base::EndsWith( - group_name, target_suffix, base::CompareCase::SENSITIVE)) { - LOG(INFO) << "Removing group " << group_name; - builder->RemoveGroupAndPartitions(group_name); - } - } - - uint64_t total_size = 0; - for (const auto& group : partition_metadata.groups) { - total_size += group.size; - } - - string expr; - uint64_t allocatable_space = builder->AllocatableSpace(); - if (!dynamic_control->IsDynamicPartitionsRetrofit()) { - allocatable_space /= 2; - expr = "half of "; - } - if (total_size > allocatable_space) { - LOG(ERROR) << "The maximum size of all groups with suffix " << target_suffix - << " (" << total_size << ") has exceeded " << expr - << " allocatable space for dynamic partitions " - << allocatable_space << "."; - return false; - } - - for (const auto& group : partition_metadata.groups) { - auto group_name_suffix = group.name + target_suffix; - if (!builder->AddGroup(group_name_suffix, group.size)) { - LOG(ERROR) << "Cannot add group " << group_name_suffix << " with size " - << group.size; - return false; - } - LOG(INFO) << "Added group " << group_name_suffix << " with size " - << group.size; - - for (const auto& partition : group.partitions) { - auto partition_name_suffix = partition.name + target_suffix; - Partition* p = builder->AddPartition( - partition_name_suffix, group_name_suffix, LP_PARTITION_ATTR_READONLY); - if (!p) { - LOG(ERROR) << "Cannot add partition " << partition_name_suffix - << " to group " << group_name_suffix; - return false; - } - if (!builder->ResizePartition(p, partition.size)) { - LOG(ERROR) << "Cannot resize partition " << partition_name_suffix - << " to size " << partition.size << ". Not enough space?"; - return false; - } - LOG(INFO) << "Added partition " << partition_name_suffix << " to group " - << group_name_suffix << " with size " << partition.size; - } - } - - auto target_device = - device_dir.Append(fs_mgr_get_super_partition_name(target_slot)).value(); - return dynamic_control->StoreMetadata( - target_device, builder.get(), target_slot); -} - -bool UnmapTargetPartitions(DynamicPartitionControlInterface* dynamic_control, - const string& target_suffix, - const PartitionMetadata& partition_metadata) { - for (const auto& group : partition_metadata.groups) { - for (const auto& partition : group.partitions) { - if (!dynamic_control->UnmapPartitionOnDeviceMapper(partition.name + - target_suffix)) { - return false; - } - } - } - return true; -} - -} // namespace - bool BootControlAndroid::InitPartitionMetadata( Slot target_slot, const PartitionMetadata& partition_metadata, @@ -417,23 +310,8 @@ bool BootControlAndroid::InitPartitionMetadata( return true; } - string target_suffix; - if (!GetSuffix(target_slot, &target_suffix)) { - return false; - } - - // Unmap all the target dynamic partitions because they would become - // inconsistent with the new metadata. - if (!UnmapTargetPartitions( - dynamic_control_.get(), target_suffix, partition_metadata)) { - return false; - } - - return UpdatePartitionMetadata(dynamic_control_.get(), - source_slot, - target_slot, - target_suffix, - partition_metadata); + return dynamic_control_->PreparePartitionsForUpdate( + source_slot, target_slot, partition_metadata); } } // namespace chromeos_update_engine diff --git a/boot_control_android_unittest.cc b/boot_control_android_unittest.cc index 94e195f8..dfcb6fb6 100644 --- a/boot_control_android_unittest.cc +++ b/boot_control_android_unittest.cc @@ -262,7 +262,7 @@ class BootControlAndroidTest : public ::testing::Test { // Fake init bootctl_ bootctl_.module_ = new NiceMock<MockBootControlHal>(); bootctl_.dynamic_control_ = - std::make_unique<NiceMock<MockDynamicPartitionControl>>(); + std::make_unique<NiceMock<MockDynamicPartitionControlAndroid>>(); ON_CALL(module(), getNumberSlots()).WillByDefault(Invoke([] { return kMaxNumSlots; @@ -297,8 +297,8 @@ class BootControlAndroidTest : public ::testing::Test { } // Return the mocked DynamicPartitionControlInterface. - NiceMock<MockDynamicPartitionControl>& dynamicControl() { - return static_cast<NiceMock<MockDynamicPartitionControl>&>( + NiceMock<MockDynamicPartitionControlAndroid>& dynamicControl() { + return static_cast<NiceMock<MockDynamicPartitionControlAndroid>&>( *bootctl_.dynamic_control_); } diff --git a/dynamic_partition_control_android.cc b/dynamic_partition_control_android.cc index b5b22df1..5a172b0b 100644 --- a/dynamic_partition_control_android.cc +++ b/dynamic_partition_control_android.cc @@ -19,16 +19,20 @@ #include <memory> #include <set> #include <string> +#include <vector> #include <android-base/properties.h> #include <android-base/strings.h> #include <base/files/file_util.h> #include <base/logging.h> +#include <base/strings/string_util.h> #include <bootloader_message/bootloader_message.h> +#include <fs_mgr.h> #include <fs_mgr_dm_linear.h> #include "update_engine/common/boot_control_interface.h" #include "update_engine/common/utils.h" +#include "update_engine/dynamic_partition_utils.h" using android::base::GetBoolProperty; using android::base::Join; @@ -37,10 +41,14 @@ using android::dm::DmDeviceState; using android::fs_mgr::CreateLogicalPartition; using android::fs_mgr::DestroyLogicalPartition; using android::fs_mgr::MetadataBuilder; +using android::fs_mgr::Partition; using android::fs_mgr::PartitionOpener; +using android::fs_mgr::SlotSuffixForSlotNumber; namespace chromeos_update_engine { +using PartitionMetadata = BootControlInterface::PartitionMetadata; + constexpr char kUseDynamicPartitions[] = "ro.boot.dynamic_partitions"; constexpr char kRetrfoitDynamicPartitions[] = "ro.boot.dynamic_partitions_retrofit"; @@ -172,6 +180,13 @@ bool DynamicPartitionControlAndroid::GetDmDevicePathByName( std::unique_ptr<MetadataBuilder> DynamicPartitionControlAndroid::LoadMetadataBuilder( + const std::string& super_device, uint32_t source_slot) { + return LoadMetadataBuilder( + super_device, source_slot, BootControlInterface::kInvalidSlot); +} + +std::unique_ptr<MetadataBuilder> +DynamicPartitionControlAndroid::LoadMetadataBuilder( const std::string& super_device, uint32_t source_slot, uint32_t target_slot) { @@ -257,4 +272,104 @@ bool DynamicPartitionControlAndroid::GetDeviceDir(std::string* out) { *out = base::FilePath(misc_device).DirName().value(); return true; } + +bool DynamicPartitionControlAndroid::PreparePartitionsForUpdate( + uint32_t source_slot, + uint32_t target_slot, + const PartitionMetadata& partition_metadata) { + const std::string target_suffix = SlotSuffixForSlotNumber(target_slot); + + // Unmap all the target dynamic partitions because they would become + // inconsistent with the new metadata. + for (const auto& group : partition_metadata.groups) { + for (const auto& partition : group.partitions) { + if (!UnmapPartitionOnDeviceMapper(partition.name + target_suffix)) { + return false; + } + } + } + + std::string device_dir_str; + if (!GetDeviceDir(&device_dir_str)) { + return false; + } + base::FilePath device_dir(device_dir_str); + auto source_device = + device_dir.Append(fs_mgr_get_super_partition_name(source_slot)).value(); + + auto builder = LoadMetadataBuilder(source_device, source_slot, target_slot); + if (builder == nullptr) { + LOG(ERROR) << "No metadata at " + << BootControlInterface::SlotName(source_slot); + return false; + } + + if (!UpdatePartitionMetadata( + builder.get(), target_slot, partition_metadata)) { + return false; + } + + auto target_device = + device_dir.Append(fs_mgr_get_super_partition_name(target_slot)).value(); + return StoreMetadata(target_device, builder.get(), target_slot); +} + +bool DynamicPartitionControlAndroid::UpdatePartitionMetadata( + MetadataBuilder* builder, + uint32_t target_slot, + const PartitionMetadata& partition_metadata) { + const std::string target_suffix = SlotSuffixForSlotNumber(target_slot); + DeleteGroupsWithSuffix(builder, target_suffix); + + uint64_t total_size = 0; + for (const auto& group : partition_metadata.groups) { + total_size += group.size; + } + + std::string expr; + uint64_t allocatable_space = builder->AllocatableSpace(); + if (!IsDynamicPartitionsRetrofit()) { + allocatable_space /= 2; + expr = "half of "; + } + if (total_size > allocatable_space) { + LOG(ERROR) << "The maximum size of all groups with suffix " << target_suffix + << " (" << total_size << ") has exceeded " << expr + << "allocatable space for dynamic partitions " + << allocatable_space << "."; + return false; + } + + for (const auto& group : partition_metadata.groups) { + auto group_name_suffix = group.name + target_suffix; + if (!builder->AddGroup(group_name_suffix, group.size)) { + LOG(ERROR) << "Cannot add group " << group_name_suffix << " with size " + << group.size; + return false; + } + LOG(INFO) << "Added group " << group_name_suffix << " with size " + << group.size; + + for (const auto& partition : group.partitions) { + auto partition_name_suffix = partition.name + target_suffix; + Partition* p = builder->AddPartition( + partition_name_suffix, group_name_suffix, LP_PARTITION_ATTR_READONLY); + if (!p) { + LOG(ERROR) << "Cannot add partition " << partition_name_suffix + << " to group " << group_name_suffix; + return false; + } + if (!builder->ResizePartition(p, partition.size)) { + LOG(ERROR) << "Cannot resize partition " << partition_name_suffix + << " to size " << partition.size << ". Not enough space?"; + return false; + } + LOG(INFO) << "Added partition " << partition_name_suffix << " to group " + << group_name_suffix << " with size " << partition.size; + } + } + + return true; +} + } // namespace chromeos_update_engine diff --git a/dynamic_partition_control_android.h b/dynamic_partition_control_android.h index 334f9bd7..e0859ed9 100644 --- a/dynamic_partition_control_android.h +++ b/dynamic_partition_control_android.h @@ -36,21 +36,48 @@ class DynamicPartitionControlAndroid : public DynamicPartitionControlInterface { uint32_t slot, bool force_writable, std::string* path) override; - bool UnmapPartitionOnDeviceMapper( - const std::string& target_partition_name) override; void Cleanup() override; bool DeviceExists(const std::string& path) override; android::dm::DmDeviceState GetState(const std::string& name) override; bool GetDmDevicePathByName(const std::string& name, std::string* path) override; std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder( + const std::string& super_device, uint32_t source_slot) override; + + bool PreparePartitionsForUpdate(uint32_t source_slot, + uint32_t target_slot, + const BootControlInterface::PartitionMetadata& + partition_metadata) override; + bool GetDeviceDir(std::string* path) override; + + protected: + // These functions are exposed for testing. + + // Unmap logical partition on device mapper. This is the reverse operation + // of MapPartitionOnDeviceMapper. + // Returns true if unmapped successfully. + virtual bool UnmapPartitionOnDeviceMapper( + const std::string& target_partition_name); + + // Retrieve metadata from |super_device| at slot |source_slot|. + // + // If |target_slot| != kInvalidSlot, before returning the metadata, this + // function modifies the metadata so that during updates, the metadata can be + // written to |target_slot|. In particular, on retrofit devices, the returned + // metadata automatically includes block devices at |target_slot|. + // + // If |target_slot| == kInvalidSlot, this function returns metadata at + // |source_slot| without modifying it. This is the same as + // LoadMetadataBuilder(const std::string&, uint32_t). + virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder( const std::string& super_device, uint32_t source_slot, - uint32_t target_slot) override; - bool StoreMetadata(const std::string& super_device, - android::fs_mgr::MetadataBuilder* builder, - uint32_t target_slot) override; - bool GetDeviceDir(std::string* path) override; + uint32_t target_slot); + + // Write metadata |builder| to |super_device| at slot |target_slot|. + virtual bool StoreMetadata(const std::string& super_device, + android::fs_mgr::MetadataBuilder* builder, + uint32_t target_slot); private: std::set<std::string> mapped_devices_; @@ -62,6 +89,13 @@ class DynamicPartitionControlAndroid : public DynamicPartitionControlInterface { bool force_writable, std::string* path); + // Update |builder| according to |partition_metadata|, assuming the device + // does not have Virtual A/B. + bool UpdatePartitionMetadata( + android::fs_mgr::MetadataBuilder* builder, + uint32_t target_slot, + const BootControlInterface::PartitionMetadata& partition_metadata); + DISALLOW_COPY_AND_ASSIGN(DynamicPartitionControlAndroid); }; diff --git a/dynamic_partition_control_interface.h b/dynamic_partition_control_interface.h index d4590f7d..12e62e08 100644 --- a/dynamic_partition_control_interface.h +++ b/dynamic_partition_control_interface.h @@ -26,6 +26,8 @@ #include <libdm/dm.h> #include <liblp/builder.h> +#include "update_engine/common/boot_control_interface.h" + namespace chromeos_update_engine { class DynamicPartitionControlInterface { @@ -52,13 +54,6 @@ class DynamicPartitionControlInterface { bool force_writable, std::string* path) = 0; - // Unmap logical partition on device mapper. This is the reverse operation - // of MapPartitionOnDeviceMapper. - // If |wait| is set, wait until the device is unmapped. - // Returns true if unmapped successfully. - virtual bool UnmapPartitionOnDeviceMapper( - const std::string& target_partition_name) = 0; - // Do necessary cleanups before destroying the object. virtual void Cleanup() = 0; @@ -77,17 +72,16 @@ class DynamicPartitionControlInterface { std::string* path) = 0; // Retrieve metadata from |super_device| at slot |source_slot|. - // On retrofit devices, if |target_slot| != kInvalidSlot, the returned - // metadata automatically includes block devices at |target_slot|. virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder( - const std::string& super_device, - uint32_t source_slot, - uint32_t target_slot) = 0; + const std::string& super_device, uint32_t source_slot) = 0; - // Write metadata |builder| to |super_device| at slot |target_slot|. - virtual bool StoreMetadata(const std::string& super_device, - android::fs_mgr::MetadataBuilder* builder, - uint32_t target_slot) = 0; + // Prepare all partitions for an update specified in |partition_metadata|. + // This is needed before calling MapPartitionOnDeviceMapper(), otherwise the + // device would be mapped in an inconsistent way. + virtual bool PreparePartitionsForUpdate( + uint32_t source_slot, + uint32_t target_slot, + const BootControlInterface::PartitionMetadata& partition_metadata) = 0; // Return a possible location for devices listed by name. virtual bool GetDeviceDir(std::string* path) = 0; diff --git a/dynamic_partition_utils.cc b/dynamic_partition_utils.cc new file mode 100644 index 00000000..f9bd886b --- /dev/null +++ b/dynamic_partition_utils.cc @@ -0,0 +1,39 @@ +// +// 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 "update_engine/dynamic_partition_utils.h" + +#include <vector> + +#include <base/logging.h> +#include <base/strings/string_util.h> + +using android::fs_mgr::MetadataBuilder; + +namespace chromeos_update_engine { + +void DeleteGroupsWithSuffix(MetadataBuilder* builder, + const std::string& suffix) { + std::vector<std::string> groups = builder->ListGroups(); + for (const auto& group_name : groups) { + if (base::EndsWith(group_name, suffix, base::CompareCase::SENSITIVE)) { + LOG(INFO) << "Removing group " << group_name; + builder->RemoveGroupAndPartitions(group_name); + } + } +} + +} // namespace chromeos_update_engine diff --git a/dynamic_partition_utils.h b/dynamic_partition_utils.h new file mode 100644 index 00000000..09fce00c --- /dev/null +++ b/dynamic_partition_utils.h @@ -0,0 +1,33 @@ +// +// 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. +// + +#ifndef UPDATE_ENGINE_DYNAMIC_PARTITION_UTILS_H_ +#define UPDATE_ENGINE_DYNAMIC_PARTITION_UTILS_H_ + +#include <string> + +#include <liblp/builder.h> + +namespace chromeos_update_engine { + +// Delete all groups (and their partitions) in |builder| that have names +// ending with |suffix|. +void DeleteGroupsWithSuffix(android::fs_mgr::MetadataBuilder* builder, + const std::string& suffix); + +} // namespace chromeos_update_engine + +#endif // UPDATE_ENGINE_DYNAMIC_PARTITION_UTILS_H_ diff --git a/mock_dynamic_partition_control.h b/mock_dynamic_partition_control.h index cdfeeccc..310e528d 100644 --- a/mock_dynamic_partition_control.h +++ b/mock_dynamic_partition_control.h @@ -21,11 +21,14 @@ #include <gmock/gmock.h> +#include "update_engine/common/boot_control_interface.h" +#include "update_engine/dynamic_partition_control_android.h" #include "update_engine/dynamic_partition_control_interface.h" namespace chromeos_update_engine { -class MockDynamicPartitionControl : public DynamicPartitionControlInterface { +class MockDynamicPartitionControlAndroid + : public DynamicPartitionControlAndroid { public: MOCK_METHOD5(MapPartitionOnDeviceMapper, bool(const std::string&, |