| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 1 | // | 
|  | 2 | // Copyright (C) 2018 The Android Open Source Project | 
|  | 3 | // | 
|  | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 5 | // you may not use this file except in compliance with the License. | 
|  | 6 | // You may obtain a copy of the License at | 
|  | 7 | // | 
|  | 8 | //      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | // | 
|  | 10 | // Unless required by applicable law or agreed to in writing, software | 
|  | 11 | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 13 | // See the License for the specific language governing permissions and | 
|  | 14 | // limitations under the License. | 
|  | 15 | // | 
|  | 16 |  | 
| Amin Hassani | ec7bc11 | 2020-10-29 16:47:58 -0700 | [diff] [blame] | 17 | #include "update_engine/aosp/dynamic_partition_control_android.h" | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 18 |  | 
| Kelvin Zhang | 91d95fa | 2020-11-05 13:52:00 -0500 | [diff] [blame] | 19 | #include <algorithm> | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 20 | #include <chrono>  // NOLINT(build/c++11) - using libsnapshot / liblp API | 
| Kelvin Zhang | 3461852 | 2020-09-28 09:21:02 -0400 | [diff] [blame] | 21 | #include <cstdint> | 
| Yifan Hong | 13d41cb | 2019-09-16 13:18:22 -0700 | [diff] [blame] | 22 | #include <map> | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 23 | #include <memory> | 
|  | 24 | #include <set> | 
|  | 25 | #include <string> | 
| Tianjie | 99d570d | 2020-06-04 14:57:19 -0700 | [diff] [blame] | 26 | #include <string_view> | 
|  | 27 | #include <utility> | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 28 | #include <vector> | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 29 |  | 
|  | 30 | #include <android-base/properties.h> | 
|  | 31 | #include <android-base/strings.h> | 
|  | 32 | #include <base/files/file_util.h> | 
|  | 33 | #include <base/logging.h> | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 34 | #include <base/strings/string_util.h> | 
| Yifan Hong | 3a1293a | 2021-04-16 13:21:20 -0700 | [diff] [blame] | 35 | #include <base/strings/stringprintf.h> | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 36 | #include <bootloader_message/bootloader_message.h> | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 37 | #include <fs_mgr.h> | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 38 | #include <fs_mgr_dm_linear.h> | 
| Yifan Hong | 3a1a561 | 2019-11-05 16:34:32 -0800 | [diff] [blame] | 39 | #include <fs_mgr_overlayfs.h> | 
| Yifan Hong | 2969290 | 2020-03-26 12:47:05 -0700 | [diff] [blame] | 40 | #include <libavb/libavb.h> | 
| Yifan Hong | 3a1a561 | 2019-11-05 16:34:32 -0800 | [diff] [blame] | 41 | #include <libdm/dm.h> | 
| Kelvin Zhang | 3461852 | 2020-09-28 09:21:02 -0400 | [diff] [blame] | 42 | #include <liblp/liblp.h> | 
|  | 43 | #include <libsnapshot/cow_writer.h> | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 44 | #include <libsnapshot/snapshot.h> | 
| Yifan Hong | f9cb449 | 2020-04-15 13:00:20 -0700 | [diff] [blame] | 45 | #include <libsnapshot/snapshot_stub.h> | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 46 |  | 
| Amin Hassani | ec7bc11 | 2020-10-29 16:47:58 -0700 | [diff] [blame] | 47 | #include "update_engine/aosp/cleanup_previous_update_action.h" | 
|  | 48 | #include "update_engine/aosp/dynamic_partition_utils.h" | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 49 | #include "update_engine/common/boot_control_interface.h" | 
| Kelvin Zhang | b9a5f61 | 2021-01-22 14:34:05 -0500 | [diff] [blame] | 50 | #include "update_engine/common/dynamic_partition_control_interface.h" | 
|  | 51 | #include "update_engine/common/platform_constants.h" | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 52 | #include "update_engine/common/utils.h" | 
| Kelvin Zhang | 21a4991 | 2021-03-12 14:28:33 -0500 | [diff] [blame] | 53 | #include "update_engine/payload_consumer/cow_writer_file_descriptor.h" | 
| Yifan Hong | 6a6d0f1 | 2020-03-11 13:20:52 -0700 | [diff] [blame] | 54 | #include "update_engine/payload_consumer/delta_performer.h" | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 55 |  | 
|  | 56 | using android::base::GetBoolProperty; | 
| Yifan Hong | 2969290 | 2020-03-26 12:47:05 -0700 | [diff] [blame] | 57 | using android::base::GetProperty; | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 58 | using android::base::Join; | 
|  | 59 | using android::dm::DeviceMapper; | 
|  | 60 | using android::dm::DmDeviceState; | 
|  | 61 | using android::fs_mgr::CreateLogicalPartition; | 
| David Anderson | bb90dfb | 2019-08-13 14:14:56 -0700 | [diff] [blame] | 62 | using android::fs_mgr::CreateLogicalPartitionParams; | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 63 | using android::fs_mgr::DestroyLogicalPartition; | 
| Yifan Hong | 2969290 | 2020-03-26 12:47:05 -0700 | [diff] [blame] | 64 | using android::fs_mgr::Fstab; | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 65 | using android::fs_mgr::MetadataBuilder; | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 66 | using android::fs_mgr::Partition; | 
| Yifan Hong | 6e706b1 | 2018-11-09 16:50:51 -0800 | [diff] [blame] | 67 | using android::fs_mgr::PartitionOpener; | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 68 | using android::fs_mgr::SlotSuffixForSlotNumber; | 
| Yifan Hong | f526156 | 2020-03-10 10:28:10 -0700 | [diff] [blame] | 69 | using android::snapshot::OptimizeSourceCopyOperation; | 
| Yifan Hong | 0850bca | 2020-01-16 15:14:07 -0800 | [diff] [blame] | 70 | using android::snapshot::Return; | 
| Yifan Hong | f033ecb | 2020-01-07 18:13:56 -0800 | [diff] [blame] | 71 | using android::snapshot::SnapshotManager; | 
| Yifan Hong | f9cb449 | 2020-04-15 13:00:20 -0700 | [diff] [blame] | 72 | using android::snapshot::SnapshotManagerStub; | 
| Yifan Hong | 2257ee1 | 2020-01-13 18:33:00 -0800 | [diff] [blame] | 73 | using android::snapshot::UpdateState; | 
| Yifan Hong | 3a1293a | 2021-04-16 13:21:20 -0700 | [diff] [blame] | 74 | using base::StringPrintf; | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 75 |  | 
|  | 76 | namespace chromeos_update_engine { | 
|  | 77 |  | 
| Yifan Hong | 6e706b1 | 2018-11-09 16:50:51 -0800 | [diff] [blame] | 78 | constexpr char kUseDynamicPartitions[] = "ro.boot.dynamic_partitions"; | 
|  | 79 | constexpr char kRetrfoitDynamicPartitions[] = | 
|  | 80 | "ro.boot.dynamic_partitions_retrofit"; | 
| Yifan Hong | 413d572 | 2019-07-23 14:21:09 -0700 | [diff] [blame] | 81 | constexpr char kVirtualAbEnabled[] = "ro.virtual_ab.enabled"; | 
|  | 82 | constexpr char kVirtualAbRetrofit[] = "ro.virtual_ab.retrofit"; | 
| Kelvin Zhang | da1b314 | 2020-09-24 17:09:02 -0400 | [diff] [blame] | 83 | constexpr char kVirtualAbCompressionEnabled[] = | 
|  | 84 | "ro.virtual_ab.compression.enabled"; | 
|  | 85 |  | 
|  | 86 | // Currently, android doesn't have a retrofit prop for VAB Compression. However, | 
|  | 87 | // struct FeatureFlag forces us to determine if a feature is 'retrofit'. So this | 
|  | 88 | // is here just to simplify code. Replace it with real retrofit prop name once | 
|  | 89 | // there is one. | 
|  | 90 | constexpr char kVirtualAbCompressionRetrofit[] = ""; | 
| Yifan Hong | 2969290 | 2020-03-26 12:47:05 -0700 | [diff] [blame] | 91 | constexpr char kPostinstallFstabPrefix[] = "ro.postinstall.fstab.prefix"; | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 92 | // Map timeout for dynamic partitions. | 
|  | 93 | constexpr std::chrono::milliseconds kMapTimeout{1000}; | 
|  | 94 | // Map timeout for dynamic partitions with snapshots. Since several devices | 
|  | 95 | // needs to be mapped, this timeout is longer than |kMapTimeout|. | 
| Kelvin Zhang | cf3280b | 2021-09-01 19:36:01 -0700 | [diff] [blame] | 96 | constexpr std::chrono::milliseconds kMapSnapshotTimeout{10000}; | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 97 |  | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 98 | DynamicPartitionControlAndroid::~DynamicPartitionControlAndroid() { | 
| Yifan Hong | bae2784 | 2019-10-24 16:56:12 -0700 | [diff] [blame] | 99 | Cleanup(); | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 100 | } | 
|  | 101 |  | 
| Yifan Hong | 186bb68 | 2019-07-23 14:04:39 -0700 | [diff] [blame] | 102 | static FeatureFlag GetFeatureFlag(const char* enable_prop, | 
|  | 103 | const char* retrofit_prop) { | 
| Kelvin Zhang | da1b314 | 2020-09-24 17:09:02 -0400 | [diff] [blame] | 104 | // Default retrofit to false if retrofit_prop is empty. | 
|  | 105 | bool retrofit = retrofit_prop && retrofit_prop[0] != '\0' && | 
|  | 106 | GetBoolProperty(retrofit_prop, false); | 
| Yifan Hong | 186bb68 | 2019-07-23 14:04:39 -0700 | [diff] [blame] | 107 | bool enabled = GetBoolProperty(enable_prop, false); | 
|  | 108 | if (retrofit && !enabled) { | 
|  | 109 | LOG(ERROR) << retrofit_prop << " is true but " << enable_prop | 
|  | 110 | << " is not. These sysprops are inconsistent. Assume that " | 
|  | 111 | << enable_prop << " is true from now on."; | 
|  | 112 | } | 
|  | 113 | if (retrofit) { | 
|  | 114 | return FeatureFlag(FeatureFlag::Value::RETROFIT); | 
|  | 115 | } | 
|  | 116 | if (enabled) { | 
|  | 117 | return FeatureFlag(FeatureFlag::Value::LAUNCH); | 
|  | 118 | } | 
|  | 119 | return FeatureFlag(FeatureFlag::Value::NONE); | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 120 | } | 
|  | 121 |  | 
| Kelvin Zhang | ebd115e | 2021-03-08 16:10:25 -0500 | [diff] [blame] | 122 | DynamicPartitionControlAndroid::DynamicPartitionControlAndroid( | 
|  | 123 | uint32_t source_slot) | 
| Yifan Hong | b38e1af | 2019-10-17 14:59:22 -0700 | [diff] [blame] | 124 | : dynamic_partitions_( | 
|  | 125 | GetFeatureFlag(kUseDynamicPartitions, kRetrfoitDynamicPartitions)), | 
| Kelvin Zhang | da1b314 | 2020-09-24 17:09:02 -0400 | [diff] [blame] | 126 | virtual_ab_(GetFeatureFlag(kVirtualAbEnabled, kVirtualAbRetrofit)), | 
|  | 127 | virtual_ab_compression_(GetFeatureFlag(kVirtualAbCompressionEnabled, | 
| Kelvin Zhang | ebd115e | 2021-03-08 16:10:25 -0500 | [diff] [blame] | 128 | kVirtualAbCompressionRetrofit)), | 
|  | 129 | source_slot_(source_slot) { | 
| Yifan Hong | b38e1af | 2019-10-17 14:59:22 -0700 | [diff] [blame] | 130 | if (GetVirtualAbFeatureFlag().IsEnabled()) { | 
| Yifan Hong | f033ecb | 2020-01-07 18:13:56 -0800 | [diff] [blame] | 131 | snapshot_ = SnapshotManager::New(); | 
| Yifan Hong | f9cb449 | 2020-04-15 13:00:20 -0700 | [diff] [blame] | 132 | } else { | 
|  | 133 | snapshot_ = SnapshotManagerStub::New(); | 
| Yifan Hong | b38e1af | 2019-10-17 14:59:22 -0700 | [diff] [blame] | 134 | } | 
| Yifan Hong | f9cb449 | 2020-04-15 13:00:20 -0700 | [diff] [blame] | 135 | CHECK(snapshot_ != nullptr) << "Cannot initialize SnapshotManager."; | 
| Yifan Hong | b38e1af | 2019-10-17 14:59:22 -0700 | [diff] [blame] | 136 | } | 
|  | 137 |  | 
| Yifan Hong | 186bb68 | 2019-07-23 14:04:39 -0700 | [diff] [blame] | 138 | FeatureFlag DynamicPartitionControlAndroid::GetDynamicPartitionsFeatureFlag() { | 
| Yifan Hong | b38e1af | 2019-10-17 14:59:22 -0700 | [diff] [blame] | 139 | return dynamic_partitions_; | 
| Yifan Hong | 6e706b1 | 2018-11-09 16:50:51 -0800 | [diff] [blame] | 140 | } | 
|  | 141 |  | 
| Yifan Hong | 413d572 | 2019-07-23 14:21:09 -0700 | [diff] [blame] | 142 | FeatureFlag DynamicPartitionControlAndroid::GetVirtualAbFeatureFlag() { | 
| Yifan Hong | b38e1af | 2019-10-17 14:59:22 -0700 | [diff] [blame] | 143 | return virtual_ab_; | 
| Yifan Hong | 413d572 | 2019-07-23 14:21:09 -0700 | [diff] [blame] | 144 | } | 
|  | 145 |  | 
| Kelvin Zhang | da1b314 | 2020-09-24 17:09:02 -0400 | [diff] [blame] | 146 | FeatureFlag | 
|  | 147 | DynamicPartitionControlAndroid::GetVirtualAbCompressionFeatureFlag() { | 
| Kelvin Zhang | b9a5f61 | 2021-01-22 14:34:05 -0500 | [diff] [blame] | 148 | if constexpr (constants::kIsRecovery) { | 
|  | 149 | // Don't attempt VABC in recovery | 
|  | 150 | return FeatureFlag(FeatureFlag::Value::NONE); | 
|  | 151 | } | 
| Kelvin Zhang | da1b314 | 2020-09-24 17:09:02 -0400 | [diff] [blame] | 152 | return virtual_ab_compression_; | 
|  | 153 | } | 
|  | 154 |  | 
| Yifan Hong | f526156 | 2020-03-10 10:28:10 -0700 | [diff] [blame] | 155 | bool DynamicPartitionControlAndroid::OptimizeOperation( | 
|  | 156 | const std::string& partition_name, | 
|  | 157 | const InstallOperation& operation, | 
|  | 158 | InstallOperation* optimized) { | 
| Alessio Balsini | 2a3b4a2 | 2019-11-25 16:46:51 +0000 | [diff] [blame] | 159 | switch (operation.type()) { | 
|  | 160 | case InstallOperation::SOURCE_COPY: | 
|  | 161 | return target_supports_snapshot_ && | 
|  | 162 | GetVirtualAbFeatureFlag().IsEnabled() && | 
| Yifan Hong | 6eec995 | 2019-12-04 13:12:01 -0800 | [diff] [blame] | 163 | mapped_devices_.count(partition_name + | 
|  | 164 | SlotSuffixForSlotNumber(target_slot_)) > 0 && | 
| Yifan Hong | f526156 | 2020-03-10 10:28:10 -0700 | [diff] [blame] | 165 | OptimizeSourceCopyOperation(operation, optimized); | 
| Alessio Balsini | 2a3b4a2 | 2019-11-25 16:46:51 +0000 | [diff] [blame] | 166 | break; | 
|  | 167 | default: | 
|  | 168 | break; | 
|  | 169 | } | 
| Alessio Balsini | 14980e2 | 2019-11-26 11:46:06 +0000 | [diff] [blame] | 170 | return false; | 
|  | 171 | } | 
|  | 172 |  | 
| Yifan Hong | 8546a71 | 2019-03-28 14:42:53 -0700 | [diff] [blame] | 173 | bool DynamicPartitionControlAndroid::MapPartitionInternal( | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 174 | const std::string& super_device, | 
|  | 175 | const std::string& target_partition_name, | 
|  | 176 | uint32_t slot, | 
| Yifan Hong | af65ef1 | 2018-10-29 11:09:06 -0700 | [diff] [blame] | 177 | bool force_writable, | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 178 | std::string* path) { | 
| David Anderson | bb90dfb | 2019-08-13 14:14:56 -0700 | [diff] [blame] | 179 | CreateLogicalPartitionParams params = { | 
|  | 180 | .block_device = super_device, | 
|  | 181 | .metadata_slot = slot, | 
|  | 182 | .partition_name = target_partition_name, | 
|  | 183 | .force_writable = force_writable, | 
| David Anderson | bb90dfb | 2019-08-13 14:14:56 -0700 | [diff] [blame] | 184 | }; | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 185 | bool success = false; | 
| Yifan Hong | f0f4a91 | 2019-09-26 17:51:33 -0700 | [diff] [blame] | 186 | if (GetVirtualAbFeatureFlag().IsEnabled() && target_supports_snapshot_ && | 
| Yifan Hong | 4d7c5eb | 2020-04-03 11:31:50 -0700 | [diff] [blame] | 187 | force_writable && ExpectMetadataMounted()) { | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 188 | // Only target partitions are mapped with force_writable. On Virtual | 
|  | 189 | // A/B devices, target partitions may overlap with source partitions, so | 
|  | 190 | // they must be mapped with snapshot. | 
| Yifan Hong | 4d7c5eb | 2020-04-03 11:31:50 -0700 | [diff] [blame] | 191 | // One exception is when /metadata is not mounted. Fallback to | 
|  | 192 | // CreateLogicalPartition as snapshots are not created in the first place. | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 193 | params.timeout_ms = kMapSnapshotTimeout; | 
|  | 194 | success = snapshot_->MapUpdateSnapshot(params, path); | 
|  | 195 | } else { | 
|  | 196 | params.timeout_ms = kMapTimeout; | 
|  | 197 | success = CreateLogicalPartition(params, path); | 
|  | 198 | } | 
| David Anderson | bb90dfb | 2019-08-13 14:14:56 -0700 | [diff] [blame] | 199 |  | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 200 | if (!success) { | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 201 | LOG(ERROR) << "Cannot map " << target_partition_name << " in " | 
|  | 202 | << super_device << " on device mapper."; | 
|  | 203 | return false; | 
|  | 204 | } | 
|  | 205 | LOG(INFO) << "Succesfully mapped " << target_partition_name | 
| Yifan Hong | af65ef1 | 2018-10-29 11:09:06 -0700 | [diff] [blame] | 206 | << " to device mapper (force_writable = " << force_writable | 
|  | 207 | << "); device path at " << *path; | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 208 | mapped_devices_.insert(target_partition_name); | 
|  | 209 | return true; | 
|  | 210 | } | 
|  | 211 |  | 
| Yifan Hong | 8546a71 | 2019-03-28 14:42:53 -0700 | [diff] [blame] | 212 | bool DynamicPartitionControlAndroid::MapPartitionOnDeviceMapper( | 
|  | 213 | const std::string& super_device, | 
|  | 214 | const std::string& target_partition_name, | 
|  | 215 | uint32_t slot, | 
|  | 216 | bool force_writable, | 
|  | 217 | std::string* path) { | 
|  | 218 | DmDeviceState state = GetState(target_partition_name); | 
|  | 219 | if (state == DmDeviceState::ACTIVE) { | 
|  | 220 | if (mapped_devices_.find(target_partition_name) != mapped_devices_.end()) { | 
|  | 221 | if (GetDmDevicePathByName(target_partition_name, path)) { | 
|  | 222 | LOG(INFO) << target_partition_name | 
|  | 223 | << " is mapped on device mapper: " << *path; | 
|  | 224 | return true; | 
|  | 225 | } | 
|  | 226 | LOG(ERROR) << target_partition_name << " is mapped but path is unknown."; | 
|  | 227 | return false; | 
|  | 228 | } | 
|  | 229 | // If target_partition_name is not in mapped_devices_ but state is ACTIVE, | 
|  | 230 | // the device might be mapped incorrectly before. Attempt to unmap it. | 
|  | 231 | // Note that for source partitions, if GetState() == ACTIVE, callers (e.g. | 
|  | 232 | // BootControlAndroid) should not call MapPartitionOnDeviceMapper, but | 
|  | 233 | // should directly call GetDmDevicePathByName. | 
| David Anderson | 4c891c9 | 2019-06-21 17:45:23 -0700 | [diff] [blame] | 234 | if (!UnmapPartitionOnDeviceMapper(target_partition_name)) { | 
| Yifan Hong | 8546a71 | 2019-03-28 14:42:53 -0700 | [diff] [blame] | 235 | LOG(ERROR) << target_partition_name | 
|  | 236 | << " is mapped before the update, and it cannot be unmapped."; | 
|  | 237 | return false; | 
|  | 238 | } | 
|  | 239 | state = GetState(target_partition_name); | 
|  | 240 | if (state != DmDeviceState::INVALID) { | 
|  | 241 | LOG(ERROR) << target_partition_name << " is unmapped but state is " | 
|  | 242 | << static_cast<std::underlying_type_t<DmDeviceState>>(state); | 
|  | 243 | return false; | 
|  | 244 | } | 
|  | 245 | } | 
|  | 246 | if (state == DmDeviceState::INVALID) { | 
|  | 247 | return MapPartitionInternal( | 
|  | 248 | super_device, target_partition_name, slot, force_writable, path); | 
|  | 249 | } | 
|  | 250 |  | 
|  | 251 | LOG(ERROR) << target_partition_name | 
|  | 252 | << " is mapped on device mapper but state is unknown: " | 
|  | 253 | << static_cast<std::underlying_type_t<DmDeviceState>>(state); | 
|  | 254 | return false; | 
|  | 255 | } | 
|  | 256 |  | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 257 | bool DynamicPartitionControlAndroid::UnmapPartitionOnDeviceMapper( | 
| David Anderson | 4c891c9 | 2019-06-21 17:45:23 -0700 | [diff] [blame] | 258 | const std::string& target_partition_name) { | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 259 | if (DeviceMapper::Instance().GetState(target_partition_name) != | 
|  | 260 | DmDeviceState::INVALID) { | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 261 | // Partitions at target slot on non-Virtual A/B devices are mapped as | 
|  | 262 | // dm-linear. Also, on Virtual A/B devices, system_other may be mapped for | 
|  | 263 | // preopt apps as dm-linear. | 
|  | 264 | // Call DestroyLogicalPartition to handle these cases. | 
|  | 265 | bool success = DestroyLogicalPartition(target_partition_name); | 
|  | 266 |  | 
|  | 267 | // On a Virtual A/B device, |target_partition_name| may be a leftover from | 
|  | 268 | // a paused update. Clean up any underlying devices. | 
| Yifan Hong | 4d7c5eb | 2020-04-03 11:31:50 -0700 | [diff] [blame] | 269 | if (ExpectMetadataMounted()) { | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 270 | success &= snapshot_->UnmapUpdateSnapshot(target_partition_name); | 
| Yifan Hong | 4d7c5eb | 2020-04-03 11:31:50 -0700 | [diff] [blame] | 271 | } else { | 
|  | 272 | LOG(INFO) << "Skip UnmapUpdateSnapshot(" << target_partition_name | 
|  | 273 | << ") because metadata is not mounted"; | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 274 | } | 
|  | 275 |  | 
|  | 276 | if (!success) { | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 277 | LOG(ERROR) << "Cannot unmap " << target_partition_name | 
|  | 278 | << " from device mapper."; | 
|  | 279 | return false; | 
|  | 280 | } | 
|  | 281 | LOG(INFO) << "Successfully unmapped " << target_partition_name | 
|  | 282 | << " from device mapper."; | 
|  | 283 | } | 
|  | 284 | mapped_devices_.erase(target_partition_name); | 
|  | 285 | return true; | 
|  | 286 | } | 
|  | 287 |  | 
| Kelvin Zhang | 9d87d6d | 2020-10-23 17:03:59 -0400 | [diff] [blame] | 288 | bool DynamicPartitionControlAndroid::UnmapAllPartitions() { | 
| Kelvin Zhang | f7ef12a | 2021-03-22 12:59:06 -0400 | [diff] [blame] | 289 | snapshot_->UnmapAllSnapshots(); | 
| Tao Bao | 8c4d008 | 2019-08-08 08:56:16 -0700 | [diff] [blame] | 290 | if (mapped_devices_.empty()) { | 
| Kelvin Zhang | 9d87d6d | 2020-10-23 17:03:59 -0400 | [diff] [blame] | 291 | return false; | 
| Tao Bao | 8c4d008 | 2019-08-08 08:56:16 -0700 | [diff] [blame] | 292 | } | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 293 | // UnmapPartitionOnDeviceMapper removes objects from mapped_devices_, hence | 
|  | 294 | // a copy is needed for the loop. | 
|  | 295 | std::set<std::string> mapped = mapped_devices_; | 
|  | 296 | LOG(INFO) << "Destroying [" << Join(mapped, ", ") << "] from device mapper"; | 
|  | 297 | for (const auto& partition_name : mapped) { | 
| David Anderson | 4c891c9 | 2019-06-21 17:45:23 -0700 | [diff] [blame] | 298 | ignore_result(UnmapPartitionOnDeviceMapper(partition_name)); | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 299 | } | 
| Kelvin Zhang | 9d87d6d | 2020-10-23 17:03:59 -0400 | [diff] [blame] | 300 | return true; | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 301 | } | 
|  | 302 |  | 
|  | 303 | void DynamicPartitionControlAndroid::Cleanup() { | 
| Yifan Hong | bae2784 | 2019-10-24 16:56:12 -0700 | [diff] [blame] | 304 | UnmapAllPartitions(); | 
|  | 305 | metadata_device_.reset(); | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 306 | } | 
|  | 307 |  | 
|  | 308 | bool DynamicPartitionControlAndroid::DeviceExists(const std::string& path) { | 
|  | 309 | return base::PathExists(base::FilePath(path)); | 
|  | 310 | } | 
|  | 311 |  | 
|  | 312 | android::dm::DmDeviceState DynamicPartitionControlAndroid::GetState( | 
|  | 313 | const std::string& name) { | 
|  | 314 | return DeviceMapper::Instance().GetState(name); | 
|  | 315 | } | 
|  | 316 |  | 
|  | 317 | bool DynamicPartitionControlAndroid::GetDmDevicePathByName( | 
|  | 318 | const std::string& name, std::string* path) { | 
|  | 319 | return DeviceMapper::Instance().GetDmDevicePathByName(name, path); | 
|  | 320 | } | 
|  | 321 |  | 
|  | 322 | std::unique_ptr<MetadataBuilder> | 
|  | 323 | DynamicPartitionControlAndroid::LoadMetadataBuilder( | 
| Tianjie | 24f9609 | 2020-06-30 12:26:25 -0700 | [diff] [blame] | 324 | const std::string& super_device, uint32_t slot) { | 
|  | 325 | auto builder = MetadataBuilder::New(PartitionOpener(), super_device, slot); | 
|  | 326 | if (builder == nullptr) { | 
|  | 327 | LOG(WARNING) << "No metadata slot " << BootControlInterface::SlotName(slot) | 
|  | 328 | << " in " << super_device; | 
|  | 329 | return nullptr; | 
|  | 330 | } | 
|  | 331 | LOG(INFO) << "Loaded metadata from slot " | 
|  | 332 | << BootControlInterface::SlotName(slot) << " in " << super_device; | 
|  | 333 | return builder; | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 334 | } | 
|  | 335 |  | 
|  | 336 | std::unique_ptr<MetadataBuilder> | 
|  | 337 | DynamicPartitionControlAndroid::LoadMetadataBuilder( | 
| Yifan Hong | 6e706b1 | 2018-11-09 16:50:51 -0800 | [diff] [blame] | 338 | const std::string& super_device, | 
|  | 339 | uint32_t source_slot, | 
|  | 340 | uint32_t target_slot) { | 
| Tianjie | 24f9609 | 2020-06-30 12:26:25 -0700 | [diff] [blame] | 341 | bool always_keep_source_slot = !target_supports_snapshot_; | 
|  | 342 | auto builder = MetadataBuilder::NewForUpdate(PartitionOpener(), | 
|  | 343 | super_device, | 
|  | 344 | source_slot, | 
|  | 345 | target_slot, | 
|  | 346 | always_keep_source_slot); | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 347 | if (builder == nullptr) { | 
|  | 348 | LOG(WARNING) << "No metadata slot " | 
|  | 349 | << BootControlInterface::SlotName(source_slot) << " in " | 
|  | 350 | << super_device; | 
| Yifan Hong | f48a005 | 2018-10-29 16:30:43 -0700 | [diff] [blame] | 351 | return nullptr; | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 352 | } | 
| Tianjie | 24f9609 | 2020-06-30 12:26:25 -0700 | [diff] [blame] | 353 | LOG(INFO) << "Created metadata for new update from slot " | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 354 | << BootControlInterface::SlotName(source_slot) << " in " | 
|  | 355 | << super_device; | 
|  | 356 | return builder; | 
|  | 357 | } | 
|  | 358 |  | 
|  | 359 | bool DynamicPartitionControlAndroid::StoreMetadata( | 
|  | 360 | const std::string& super_device, | 
|  | 361 | MetadataBuilder* builder, | 
|  | 362 | uint32_t target_slot) { | 
|  | 363 | auto metadata = builder->Export(); | 
|  | 364 | if (metadata == nullptr) { | 
|  | 365 | LOG(ERROR) << "Cannot export metadata to slot " | 
|  | 366 | << BootControlInterface::SlotName(target_slot) << " in " | 
|  | 367 | << super_device; | 
|  | 368 | return false; | 
|  | 369 | } | 
|  | 370 |  | 
| Yifan Hong | 186bb68 | 2019-07-23 14:04:39 -0700 | [diff] [blame] | 371 | if (GetDynamicPartitionsFeatureFlag().IsRetrofit()) { | 
| Yifan Hong | 6e706b1 | 2018-11-09 16:50:51 -0800 | [diff] [blame] | 372 | if (!FlashPartitionTable(super_device, *metadata)) { | 
|  | 373 | LOG(ERROR) << "Cannot write metadata to " << super_device; | 
|  | 374 | return false; | 
|  | 375 | } | 
|  | 376 | LOG(INFO) << "Written metadata to " << super_device; | 
|  | 377 | } else { | 
|  | 378 | if (!UpdatePartitionTable(super_device, *metadata, target_slot)) { | 
|  | 379 | LOG(ERROR) << "Cannot write metadata to slot " | 
|  | 380 | << BootControlInterface::SlotName(target_slot) << " in " | 
|  | 381 | << super_device; | 
|  | 382 | return false; | 
|  | 383 | } | 
|  | 384 | LOG(INFO) << "Copied metadata to slot " | 
|  | 385 | << BootControlInterface::SlotName(target_slot) << " in " | 
|  | 386 | << super_device; | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 387 | } | 
|  | 388 |  | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 389 | return true; | 
|  | 390 | } | 
|  | 391 |  | 
|  | 392 | bool DynamicPartitionControlAndroid::GetDeviceDir(std::string* out) { | 
|  | 393 | // We can't use fs_mgr to look up |partition_name| because fstab | 
|  | 394 | // doesn't list every slot partition (it uses the slotselect option | 
|  | 395 | // to mask the suffix). | 
|  | 396 | // | 
|  | 397 | // We can however assume that there's an entry for the /misc mount | 
|  | 398 | // point and use that to get the device file for the misc | 
|  | 399 | // partition. This helps us locate the disk that |partition_name| | 
|  | 400 | // resides on. From there we'll assume that a by-name scheme is used | 
|  | 401 | // so we can just replace the trailing "misc" by the given | 
|  | 402 | // |partition_name| and suffix corresponding to |slot|, e.g. | 
|  | 403 | // | 
|  | 404 | //   /dev/block/platform/soc.0/7824900.sdhci/by-name/misc -> | 
|  | 405 | //   /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a | 
|  | 406 | // | 
|  | 407 | // If needed, it's possible to relax the by-name assumption in the | 
|  | 408 | // future by trawling /sys/block looking for the appropriate sibling | 
|  | 409 | // of misc and then finding an entry in /dev matching the sysfs | 
|  | 410 | // entry. | 
|  | 411 |  | 
|  | 412 | std::string err, misc_device = get_bootloader_message_blk_device(&err); | 
|  | 413 | if (misc_device.empty()) { | 
|  | 414 | LOG(ERROR) << "Unable to get misc block device: " << err; | 
|  | 415 | return false; | 
|  | 416 | } | 
|  | 417 |  | 
|  | 418 | if (!utils::IsSymlink(misc_device.c_str())) { | 
|  | 419 | LOG(ERROR) << "Device file " << misc_device << " for /misc " | 
|  | 420 | << "is not a symlink."; | 
|  | 421 | return false; | 
|  | 422 | } | 
|  | 423 | *out = base::FilePath(misc_device).DirName().value(); | 
|  | 424 | return true; | 
|  | 425 | } | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 426 |  | 
|  | 427 | bool DynamicPartitionControlAndroid::PreparePartitionsForUpdate( | 
|  | 428 | uint32_t source_slot, | 
|  | 429 | uint32_t target_slot, | 
| Yifan Hong | f0f4a91 | 2019-09-26 17:51:33 -0700 | [diff] [blame] | 430 | const DeltaArchiveManifest& manifest, | 
| Yifan Hong | f033ecb | 2020-01-07 18:13:56 -0800 | [diff] [blame] | 431 | bool update, | 
|  | 432 | uint64_t* required_size) { | 
| Yifan Hong | 6eec995 | 2019-12-04 13:12:01 -0800 | [diff] [blame] | 433 | source_slot_ = source_slot; | 
|  | 434 | target_slot_ = target_slot; | 
| Yifan Hong | f033ecb | 2020-01-07 18:13:56 -0800 | [diff] [blame] | 435 | if (required_size != nullptr) { | 
|  | 436 | *required_size = 0; | 
|  | 437 | } | 
| Yifan Hong | 6eec995 | 2019-12-04 13:12:01 -0800 | [diff] [blame] | 438 |  | 
| Yifan Hong | 3a1a561 | 2019-11-05 16:34:32 -0800 | [diff] [blame] | 439 | if (fs_mgr_overlayfs_is_setup()) { | 
|  | 440 | // Non DAP devices can use overlayfs as well. | 
|  | 441 | LOG(WARNING) | 
|  | 442 | << "overlayfs overrides are active and can interfere with our " | 
|  | 443 | "resources.\n" | 
|  | 444 | << "run adb enable-verity to deactivate if required and try again."; | 
|  | 445 | } | 
|  | 446 |  | 
| Yifan Hong | 4d7c5eb | 2020-04-03 11:31:50 -0700 | [diff] [blame] | 447 | // If metadata is erased but not formatted, it is possible to not mount | 
|  | 448 | // it in recovery. It is acceptable to skip mounting and choose fallback path | 
|  | 449 | // (PrepareDynamicPartitionsForUpdate) when sideloading full OTAs. | 
|  | 450 | TEST_AND_RETURN_FALSE(EnsureMetadataMounted() || IsRecovery()); | 
| Yifan Hong | 2969290 | 2020-03-26 12:47:05 -0700 | [diff] [blame] | 451 |  | 
|  | 452 | if (update) { | 
|  | 453 | TEST_AND_RETURN_FALSE(EraseSystemOtherAvbFooter(source_slot, target_slot)); | 
|  | 454 | } | 
|  | 455 |  | 
| Yifan Hong | 3a1a561 | 2019-11-05 16:34:32 -0800 | [diff] [blame] | 456 | if (!GetDynamicPartitionsFeatureFlag().IsEnabled()) { | 
|  | 457 | return true; | 
|  | 458 | } | 
|  | 459 |  | 
|  | 460 | if (target_slot == source_slot) { | 
|  | 461 | LOG(ERROR) << "Cannot call PreparePartitionsForUpdate on current slot."; | 
|  | 462 | return false; | 
|  | 463 | } | 
|  | 464 |  | 
| Yifan Hong | f6f75c2 | 2020-07-31 15:20:25 -0700 | [diff] [blame] | 465 | if (!SetTargetBuildVars(manifest)) { | 
|  | 466 | return false; | 
|  | 467 | } | 
|  | 468 |  | 
| Yifan Hong | 3a1a561 | 2019-11-05 16:34:32 -0800 | [diff] [blame] | 469 | // Although the current build supports dynamic partitions, the given payload | 
|  | 470 | // doesn't use it for target partitions. This could happen when applying a | 
|  | 471 | // retrofit update. Skip updating the partition metadata for the target slot. | 
| Yifan Hong | 3a1a561 | 2019-11-05 16:34:32 -0800 | [diff] [blame] | 472 | if (!is_target_dynamic_) { | 
|  | 473 | return true; | 
|  | 474 | } | 
|  | 475 |  | 
| Yifan Hong | f0f4a91 | 2019-09-26 17:51:33 -0700 | [diff] [blame] | 476 | if (!update) | 
|  | 477 | return true; | 
|  | 478 |  | 
| Yifan Hong | bae2784 | 2019-10-24 16:56:12 -0700 | [diff] [blame] | 479 | bool delete_source = false; | 
|  | 480 |  | 
| Yifan Hong | 6d88856 | 2019-10-01 13:00:31 -0700 | [diff] [blame] | 481 | if (GetVirtualAbFeatureFlag().IsEnabled()) { | 
|  | 482 | // On Virtual A/B device, either CancelUpdate() or BeginUpdate() must be | 
|  | 483 | // called before calling UnmapUpdateSnapshot. | 
|  | 484 | // - If target_supports_snapshot_, PrepareSnapshotPartitionsForUpdate() | 
|  | 485 | //   calls BeginUpdate() which resets update state | 
| Yifan Hong | bae2784 | 2019-10-24 16:56:12 -0700 | [diff] [blame] | 486 | // - If !target_supports_snapshot_ or PrepareSnapshotPartitionsForUpdate | 
|  | 487 | //   failed in recovery, explicitly CancelUpdate(). | 
| Yifan Hong | 6d88856 | 2019-10-01 13:00:31 -0700 | [diff] [blame] | 488 | if (target_supports_snapshot_) { | 
| Yifan Hong | bae2784 | 2019-10-24 16:56:12 -0700 | [diff] [blame] | 489 | if (PrepareSnapshotPartitionsForUpdate( | 
|  | 490 | source_slot, target_slot, manifest, required_size)) { | 
|  | 491 | return true; | 
|  | 492 | } | 
|  | 493 |  | 
|  | 494 | // Virtual A/B device doing Virtual A/B update in Android mode must use | 
|  | 495 | // snapshots. | 
|  | 496 | if (!IsRecovery()) { | 
|  | 497 | LOG(ERROR) << "PrepareSnapshotPartitionsForUpdate failed in Android " | 
|  | 498 | << "mode"; | 
|  | 499 | return false; | 
|  | 500 | } | 
|  | 501 |  | 
|  | 502 | delete_source = true; | 
|  | 503 | LOG(INFO) << "PrepareSnapshotPartitionsForUpdate failed in recovery. " | 
|  | 504 | << "Attempt to overwrite existing partitions if possible"; | 
|  | 505 | } else { | 
|  | 506 | // Downgrading to an non-Virtual A/B build or is secondary OTA. | 
|  | 507 | LOG(INFO) << "Using regular A/B on Virtual A/B because package disabled " | 
|  | 508 | << "snapshots."; | 
| Yifan Hong | 6d88856 | 2019-10-01 13:00:31 -0700 | [diff] [blame] | 509 | } | 
| Yifan Hong | bae2784 | 2019-10-24 16:56:12 -0700 | [diff] [blame] | 510 |  | 
| Yifan Hong | 4d7c5eb | 2020-04-03 11:31:50 -0700 | [diff] [blame] | 511 | // In recovery, if /metadata is not mounted, it is likely that metadata | 
|  | 512 | // partition is erased and not formatted yet. After sideloading, when | 
|  | 513 | // rebooting into the new version, init will erase metadata partition, | 
|  | 514 | // hence the failure of CancelUpdate() can be ignored here. | 
|  | 515 | // However, if metadata is mounted and CancelUpdate fails, sideloading | 
|  | 516 | // should not proceed because during next boot, snapshots will overlay on | 
|  | 517 | // the devices incorrectly. | 
|  | 518 | if (ExpectMetadataMounted()) { | 
|  | 519 | TEST_AND_RETURN_FALSE(snapshot_->CancelUpdate()); | 
|  | 520 | } else { | 
|  | 521 | LOG(INFO) << "Skip canceling previous update because metadata is not " | 
|  | 522 | << "mounted"; | 
| Yifan Hong | 6d88856 | 2019-10-01 13:00:31 -0700 | [diff] [blame] | 523 | } | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 524 | } | 
| Yifan Hong | bae2784 | 2019-10-24 16:56:12 -0700 | [diff] [blame] | 525 |  | 
| Tianjie | 24f9609 | 2020-06-30 12:26:25 -0700 | [diff] [blame] | 526 | // TODO(xunchang) support partial update on non VAB enabled devices. | 
| Yifan Hong | 5c5743f | 2020-04-16 12:59:07 -0700 | [diff] [blame] | 527 | TEST_AND_RETURN_FALSE(PrepareDynamicPartitionsForUpdate( | 
|  | 528 | source_slot, target_slot, manifest, delete_source)); | 
|  | 529 |  | 
|  | 530 | if (required_size != nullptr) { | 
|  | 531 | *required_size = 0; | 
|  | 532 | } | 
|  | 533 | return true; | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 534 | } | 
|  | 535 |  | 
| Yifan Hong | f6f75c2 | 2020-07-31 15:20:25 -0700 | [diff] [blame] | 536 | bool DynamicPartitionControlAndroid::SetTargetBuildVars( | 
|  | 537 | const DeltaArchiveManifest& manifest) { | 
|  | 538 | // Precondition: current build supports dynamic partition. | 
|  | 539 | CHECK(GetDynamicPartitionsFeatureFlag().IsEnabled()); | 
|  | 540 |  | 
|  | 541 | bool is_target_dynamic = | 
|  | 542 | !manifest.dynamic_partition_metadata().groups().empty(); | 
|  | 543 | bool target_supports_snapshot = | 
|  | 544 | manifest.dynamic_partition_metadata().snapshot_enabled(); | 
|  | 545 |  | 
|  | 546 | if (manifest.partial_update()) { | 
|  | 547 | // Partial updates requires DAP. On partial updates that does not involve | 
|  | 548 | // dynamic partitions, groups() can be empty, so also assume | 
|  | 549 | // is_target_dynamic in this case. This assumption should be safe because we | 
|  | 550 | // also check target_supports_snapshot below, which presumably also implies | 
|  | 551 | // target build supports dynamic partition. | 
|  | 552 | if (!is_target_dynamic) { | 
|  | 553 | LOG(INFO) << "Assuming target build supports dynamic partitions for " | 
|  | 554 | "partial updates."; | 
|  | 555 | is_target_dynamic = true; | 
|  | 556 | } | 
|  | 557 |  | 
|  | 558 | // Partial updates requires Virtual A/B. Double check that both current | 
|  | 559 | // build and target build supports Virtual A/B. | 
|  | 560 | if (!GetVirtualAbFeatureFlag().IsEnabled()) { | 
|  | 561 | LOG(ERROR) << "Partial update cannot be applied on a device that does " | 
|  | 562 | "not support snapshots."; | 
|  | 563 | return false; | 
|  | 564 | } | 
|  | 565 | if (!target_supports_snapshot) { | 
|  | 566 | LOG(ERROR) << "Cannot apply partial update to a build that does not " | 
|  | 567 | "support snapshots."; | 
|  | 568 | return false; | 
|  | 569 | } | 
|  | 570 | } | 
|  | 571 |  | 
|  | 572 | // Store the flags. | 
|  | 573 | is_target_dynamic_ = is_target_dynamic; | 
|  | 574 | // If !is_target_dynamic_, leave target_supports_snapshot_ unset because | 
|  | 575 | // snapshots would not work without dynamic partition. | 
|  | 576 | if (is_target_dynamic_) { | 
|  | 577 | target_supports_snapshot_ = target_supports_snapshot; | 
|  | 578 | } | 
|  | 579 | return true; | 
|  | 580 | } | 
|  | 581 |  | 
| Yifan Hong | 2969290 | 2020-03-26 12:47:05 -0700 | [diff] [blame] | 582 | namespace { | 
|  | 583 | // Try our best to erase AVB footer. | 
|  | 584 | class AvbFooterEraser { | 
|  | 585 | public: | 
|  | 586 | explicit AvbFooterEraser(const std::string& path) : path_(path) {} | 
|  | 587 | bool Erase() { | 
|  | 588 | // Try to mark the block device read-only. Ignore any | 
|  | 589 | // failure since this won't work when passing regular files. | 
|  | 590 | ignore_result(utils::SetBlockDeviceReadOnly(path_, false /* readonly */)); | 
|  | 591 |  | 
|  | 592 | fd_.reset(new EintrSafeFileDescriptor()); | 
|  | 593 | int flags = O_WRONLY | O_TRUNC | O_CLOEXEC | O_SYNC; | 
|  | 594 | TEST_AND_RETURN_FALSE(fd_->Open(path_.c_str(), flags)); | 
|  | 595 |  | 
|  | 596 | // Need to write end-AVB_FOOTER_SIZE to end. | 
|  | 597 | static_assert(AVB_FOOTER_SIZE > 0); | 
|  | 598 | off64_t offset = fd_->Seek(-AVB_FOOTER_SIZE, SEEK_END); | 
|  | 599 | TEST_AND_RETURN_FALSE_ERRNO(offset >= 0); | 
|  | 600 | uint64_t write_size = AVB_FOOTER_SIZE; | 
|  | 601 | LOG(INFO) << "Zeroing " << path_ << " @ [" << offset << ", " | 
|  | 602 | << (offset + write_size) << "] (" << write_size << " bytes)"; | 
|  | 603 | brillo::Blob zeros(write_size); | 
|  | 604 | TEST_AND_RETURN_FALSE(utils::WriteAll(fd_, zeros.data(), zeros.size())); | 
|  | 605 | return true; | 
|  | 606 | } | 
|  | 607 | ~AvbFooterEraser() { | 
|  | 608 | TEST_AND_RETURN(fd_ != nullptr && fd_->IsOpen()); | 
|  | 609 | if (!fd_->Close()) { | 
|  | 610 | LOG(WARNING) << "Failed to close fd for " << path_; | 
|  | 611 | } | 
|  | 612 | } | 
|  | 613 |  | 
|  | 614 | private: | 
|  | 615 | std::string path_; | 
|  | 616 | FileDescriptorPtr fd_; | 
|  | 617 | }; | 
|  | 618 |  | 
|  | 619 | }  // namespace | 
|  | 620 |  | 
|  | 621 | std::optional<bool> | 
|  | 622 | DynamicPartitionControlAndroid::IsAvbEnabledOnSystemOther() { | 
|  | 623 | auto prefix = GetProperty(kPostinstallFstabPrefix, ""); | 
|  | 624 | if (prefix.empty()) { | 
|  | 625 | LOG(WARNING) << "Cannot get " << kPostinstallFstabPrefix; | 
|  | 626 | return std::nullopt; | 
|  | 627 | } | 
|  | 628 | auto path = base::FilePath(prefix).Append("etc/fstab.postinstall").value(); | 
|  | 629 | return IsAvbEnabledInFstab(path); | 
|  | 630 | } | 
|  | 631 |  | 
|  | 632 | std::optional<bool> DynamicPartitionControlAndroid::IsAvbEnabledInFstab( | 
|  | 633 | const std::string& path) { | 
|  | 634 | Fstab fstab; | 
|  | 635 | if (!ReadFstabFromFile(path, &fstab)) { | 
| Yifan Hong | 93cde30 | 2020-04-27 12:59:29 -0700 | [diff] [blame] | 636 | PLOG(WARNING) << "Cannot read fstab from " << path; | 
|  | 637 | if (errno == ENOENT) { | 
|  | 638 | return false; | 
|  | 639 | } | 
| Yifan Hong | 2969290 | 2020-03-26 12:47:05 -0700 | [diff] [blame] | 640 | return std::nullopt; | 
|  | 641 | } | 
|  | 642 | for (const auto& entry : fstab) { | 
|  | 643 | if (!entry.avb_keys.empty()) { | 
|  | 644 | return true; | 
|  | 645 | } | 
|  | 646 | } | 
|  | 647 | return false; | 
|  | 648 | } | 
|  | 649 |  | 
|  | 650 | bool DynamicPartitionControlAndroid::GetSystemOtherPath( | 
|  | 651 | uint32_t source_slot, | 
|  | 652 | uint32_t target_slot, | 
|  | 653 | const std::string& partition_name_suffix, | 
|  | 654 | std::string* path, | 
|  | 655 | bool* should_unmap) { | 
|  | 656 | path->clear(); | 
|  | 657 | *should_unmap = false; | 
|  | 658 |  | 
| P.Adarsh Reddy | 13e4195d | 2020-06-08 23:17:36 +0530 | [diff] [blame] | 659 | // Check that AVB is enabled on system_other before erasing. | 
|  | 660 | auto has_avb = IsAvbEnabledOnSystemOther(); | 
|  | 661 | TEST_AND_RETURN_FALSE(has_avb.has_value()); | 
|  | 662 | if (!has_avb.value()) { | 
|  | 663 | LOG(INFO) << "AVB is not enabled on system_other. Skip erasing."; | 
|  | 664 | return true; | 
|  | 665 | } | 
| Yifan Hong | 2969290 | 2020-03-26 12:47:05 -0700 | [diff] [blame] | 666 |  | 
| P.Adarsh Reddy | 13e4195d | 2020-06-08 23:17:36 +0530 | [diff] [blame] | 667 | if (!IsRecovery()) { | 
| Yifan Hong | 2969290 | 2020-03-26 12:47:05 -0700 | [diff] [blame] | 668 | // Found unexpected avb_keys for system_other on devices retrofitting | 
|  | 669 | // dynamic partitions. Previous crash in update_engine may leave logical | 
|  | 670 | // partitions mapped on physical system_other partition. It is difficult to | 
|  | 671 | // handle these cases. Just fail. | 
|  | 672 | if (GetDynamicPartitionsFeatureFlag().IsRetrofit()) { | 
|  | 673 | LOG(ERROR) << "Cannot erase AVB footer on system_other on devices with " | 
|  | 674 | << "retrofit dynamic partitions. They should not have AVB " | 
|  | 675 | << "enabled on system_other."; | 
|  | 676 | return false; | 
|  | 677 | } | 
|  | 678 | } | 
|  | 679 |  | 
|  | 680 | std::string device_dir_str; | 
|  | 681 | TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str)); | 
|  | 682 | base::FilePath device_dir(device_dir_str); | 
|  | 683 |  | 
|  | 684 | // On devices without dynamic partition, search for static partitions. | 
|  | 685 | if (!GetDynamicPartitionsFeatureFlag().IsEnabled()) { | 
|  | 686 | *path = device_dir.Append(partition_name_suffix).value(); | 
|  | 687 | TEST_AND_RETURN_FALSE(DeviceExists(*path)); | 
|  | 688 | return true; | 
|  | 689 | } | 
|  | 690 |  | 
|  | 691 | auto source_super_device = | 
|  | 692 | device_dir.Append(GetSuperPartitionName(source_slot)).value(); | 
|  | 693 |  | 
|  | 694 | auto builder = LoadMetadataBuilder(source_super_device, source_slot); | 
|  | 695 | if (builder == nullptr) { | 
|  | 696 | if (IsRecovery()) { | 
|  | 697 | // It might be corrupted for some reason. It should still be able to | 
|  | 698 | // sideload. | 
|  | 699 | LOG(WARNING) << "Super partition metadata cannot be read from the source " | 
|  | 700 | << "slot, skip erasing."; | 
|  | 701 | return true; | 
|  | 702 | } else { | 
|  | 703 | // Device has booted into Android mode, indicating that the super | 
|  | 704 | // partition metadata should be there. | 
|  | 705 | LOG(ERROR) << "Super partition metadata cannot be read from the source " | 
|  | 706 | << "slot. This is unexpected on devices with dynamic " | 
|  | 707 | << "partitions enabled."; | 
|  | 708 | return false; | 
|  | 709 | } | 
|  | 710 | } | 
|  | 711 | auto p = builder->FindPartition(partition_name_suffix); | 
|  | 712 | if (p == nullptr) { | 
|  | 713 | // If the source slot is flashed without system_other, it does not exist | 
|  | 714 | // in super partition metadata at source slot. It is safe to skip it. | 
|  | 715 | LOG(INFO) << "Can't find " << partition_name_suffix | 
|  | 716 | << " in metadata source slot, skip erasing."; | 
|  | 717 | return true; | 
|  | 718 | } | 
|  | 719 | // System_other created by flashing tools should be erased. | 
|  | 720 | // If partition is created by update_engine (via NewForUpdate), it is a | 
|  | 721 | // left-over partition from the previous update and does not contain | 
|  | 722 | // system_other, hence there is no need to erase. | 
|  | 723 | // Note the reverse is not necessary true. If the flag is not set, we don't | 
|  | 724 | // know if the partition is created by update_engine or by flashing tools | 
|  | 725 | // because older versions of super partition metadata does not contain this | 
|  | 726 | // flag. It is okay to erase the AVB footer anyways. | 
|  | 727 | if (p->attributes() & LP_PARTITION_ATTR_UPDATED) { | 
|  | 728 | LOG(INFO) << partition_name_suffix | 
|  | 729 | << " does not contain system_other, skip erasing."; | 
|  | 730 | return true; | 
|  | 731 | } | 
|  | 732 |  | 
| Yifan Hong | 64331b3 | 2020-05-13 16:50:40 -0700 | [diff] [blame] | 733 | if (p->size() < AVB_FOOTER_SIZE) { | 
|  | 734 | LOG(INFO) << partition_name_suffix << " has length " << p->size() | 
|  | 735 | << "( < AVB_FOOTER_SIZE " << AVB_FOOTER_SIZE | 
|  | 736 | << "), skip erasing."; | 
|  | 737 | return true; | 
|  | 738 | } | 
|  | 739 |  | 
| Yifan Hong | 2969290 | 2020-03-26 12:47:05 -0700 | [diff] [blame] | 740 | // Delete any pre-existing device with name |partition_name_suffix| and | 
|  | 741 | // also remove it from |mapped_devices_|. | 
| Yifan Hong | 4d7c5eb | 2020-04-03 11:31:50 -0700 | [diff] [blame] | 742 | // In recovery, metadata might not be mounted, and | 
|  | 743 | // UnmapPartitionOnDeviceMapper might fail. However, | 
|  | 744 | // it is unusual that system_other has already been mapped. Hence, just skip. | 
| Yifan Hong | 2969290 | 2020-03-26 12:47:05 -0700 | [diff] [blame] | 745 | TEST_AND_RETURN_FALSE(UnmapPartitionOnDeviceMapper(partition_name_suffix)); | 
|  | 746 | // Use CreateLogicalPartition directly to avoid mapping with existing | 
|  | 747 | // snapshots. | 
|  | 748 | CreateLogicalPartitionParams params = { | 
|  | 749 | .block_device = source_super_device, | 
|  | 750 | .metadata_slot = source_slot, | 
|  | 751 | .partition_name = partition_name_suffix, | 
|  | 752 | .force_writable = true, | 
|  | 753 | .timeout_ms = kMapTimeout, | 
|  | 754 | }; | 
|  | 755 | TEST_AND_RETURN_FALSE(CreateLogicalPartition(params, path)); | 
|  | 756 | *should_unmap = true; | 
|  | 757 | return true; | 
|  | 758 | } | 
|  | 759 |  | 
|  | 760 | bool DynamicPartitionControlAndroid::EraseSystemOtherAvbFooter( | 
|  | 761 | uint32_t source_slot, uint32_t target_slot) { | 
|  | 762 | LOG(INFO) << "Erasing AVB footer of system_other partition before update."; | 
|  | 763 |  | 
|  | 764 | const std::string target_suffix = SlotSuffixForSlotNumber(target_slot); | 
|  | 765 | const std::string partition_name_suffix = "system" + target_suffix; | 
|  | 766 |  | 
|  | 767 | std::string path; | 
|  | 768 | bool should_unmap = false; | 
|  | 769 |  | 
|  | 770 | TEST_AND_RETURN_FALSE(GetSystemOtherPath( | 
|  | 771 | source_slot, target_slot, partition_name_suffix, &path, &should_unmap)); | 
|  | 772 |  | 
|  | 773 | if (path.empty()) { | 
|  | 774 | return true; | 
|  | 775 | } | 
|  | 776 |  | 
|  | 777 | bool ret = AvbFooterEraser(path).Erase(); | 
|  | 778 |  | 
|  | 779 | // Delete |partition_name_suffix| from device mapper and from | 
|  | 780 | // |mapped_devices_| again so that it does not interfere with update process. | 
| Yifan Hong | 4d7c5eb | 2020-04-03 11:31:50 -0700 | [diff] [blame] | 781 | // In recovery, metadata might not be mounted, and | 
|  | 782 | // UnmapPartitionOnDeviceMapper might fail. However, DestroyLogicalPartition | 
|  | 783 | // should be called. If DestroyLogicalPartition does fail, it is still okay | 
|  | 784 | // to skip the error here and let Prepare*() fail later. | 
| Yifan Hong | 2969290 | 2020-03-26 12:47:05 -0700 | [diff] [blame] | 785 | if (should_unmap) { | 
|  | 786 | TEST_AND_RETURN_FALSE(UnmapPartitionOnDeviceMapper(partition_name_suffix)); | 
|  | 787 | } | 
|  | 788 |  | 
|  | 789 | return ret; | 
|  | 790 | } | 
|  | 791 |  | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 792 | bool DynamicPartitionControlAndroid::PrepareDynamicPartitionsForUpdate( | 
|  | 793 | uint32_t source_slot, | 
|  | 794 | uint32_t target_slot, | 
| Yifan Hong | bae2784 | 2019-10-24 16:56:12 -0700 | [diff] [blame] | 795 | const DeltaArchiveManifest& manifest, | 
|  | 796 | bool delete_source) { | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 797 | const std::string target_suffix = SlotSuffixForSlotNumber(target_slot); | 
|  | 798 |  | 
|  | 799 | // Unmap all the target dynamic partitions because they would become | 
|  | 800 | // inconsistent with the new metadata. | 
| Yifan Hong | 13d41cb | 2019-09-16 13:18:22 -0700 | [diff] [blame] | 801 | for (const auto& group : manifest.dynamic_partition_metadata().groups()) { | 
|  | 802 | for (const auto& partition_name : group.partition_names()) { | 
|  | 803 | if (!UnmapPartitionOnDeviceMapper(partition_name + target_suffix)) { | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 804 | return false; | 
|  | 805 | } | 
|  | 806 | } | 
|  | 807 | } | 
|  | 808 |  | 
|  | 809 | std::string device_dir_str; | 
| Tianjie | 9f4dc7f | 2021-03-15 16:00:50 -0700 | [diff] [blame] | 810 | TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str)); | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 811 | base::FilePath device_dir(device_dir_str); | 
|  | 812 | auto source_device = | 
| Yifan Hong | 700d7c1 | 2019-07-23 20:49:16 -0700 | [diff] [blame] | 813 | device_dir.Append(GetSuperPartitionName(source_slot)).value(); | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 814 |  | 
|  | 815 | auto builder = LoadMetadataBuilder(source_device, source_slot, target_slot); | 
|  | 816 | if (builder == nullptr) { | 
|  | 817 | LOG(ERROR) << "No metadata at " | 
|  | 818 | << BootControlInterface::SlotName(source_slot); | 
|  | 819 | return false; | 
|  | 820 | } | 
|  | 821 |  | 
| Yifan Hong | bae2784 | 2019-10-24 16:56:12 -0700 | [diff] [blame] | 822 | if (delete_source) { | 
|  | 823 | TEST_AND_RETURN_FALSE( | 
|  | 824 | DeleteSourcePartitions(builder.get(), source_slot, manifest)); | 
|  | 825 | } | 
|  | 826 |  | 
| Tianjie | 9f4dc7f | 2021-03-15 16:00:50 -0700 | [diff] [blame] | 827 | TEST_AND_RETURN_FALSE( | 
|  | 828 | UpdatePartitionMetadata(builder.get(), target_slot, manifest)); | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 829 |  | 
|  | 830 | auto target_device = | 
| Yifan Hong | 700d7c1 | 2019-07-23 20:49:16 -0700 | [diff] [blame] | 831 | device_dir.Append(GetSuperPartitionName(target_slot)).value(); | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 832 | return StoreMetadata(target_device, builder.get(), target_slot); | 
|  | 833 | } | 
|  | 834 |  | 
| Yifan Hong | 3a1293a | 2021-04-16 13:21:20 -0700 | [diff] [blame] | 835 | DynamicPartitionControlAndroid::SpaceLimit | 
|  | 836 | DynamicPartitionControlAndroid::GetSpaceLimit(bool use_snapshot) { | 
|  | 837 | // On device retrofitting dynamic partitions, allocatable_space = "super", | 
|  | 838 | // where "super" is the sum of all block devices for that slot. Since block | 
|  | 839 | // devices are dedicated for the corresponding slot, there's no need to halve | 
|  | 840 | // the allocatable space. | 
|  | 841 | if (GetDynamicPartitionsFeatureFlag().IsRetrofit()) | 
|  | 842 | return SpaceLimit::ERROR_IF_EXCEEDED_SUPER; | 
|  | 843 |  | 
|  | 844 | // On device launching dynamic partitions w/o VAB, regardless of recovery | 
|  | 845 | // sideload, super partition must be big enough to hold both A and B slots of | 
|  | 846 | // groups. Hence, | 
|  | 847 | // allocatable_space = super / 2 | 
|  | 848 | if (!GetVirtualAbFeatureFlag().IsEnabled()) | 
|  | 849 | return SpaceLimit::ERROR_IF_EXCEEDED_HALF_OF_SUPER; | 
|  | 850 |  | 
|  | 851 | // Source build supports VAB. Super partition must be big enough to hold | 
|  | 852 | // one slot of groups (ERROR_IF_EXCEEDED_SUPER). However, there are cases | 
|  | 853 | // where additional warning messages needs to be written. | 
|  | 854 |  | 
|  | 855 | // If using snapshot updates, implying that target build also uses VAB, | 
|  | 856 | // allocatable_space = super | 
|  | 857 | if (use_snapshot) | 
|  | 858 | return SpaceLimit::ERROR_IF_EXCEEDED_SUPER; | 
|  | 859 |  | 
|  | 860 | // Source build supports VAB but not using snapshot updates. There are | 
|  | 861 | // several cases, as listed below. | 
|  | 862 | // Sideloading: allocatable_space = super. | 
|  | 863 | if (IsRecovery()) | 
|  | 864 | return SpaceLimit::ERROR_IF_EXCEEDED_SUPER; | 
|  | 865 |  | 
|  | 866 | // On launch VAB device, this implies secondary payload. | 
|  | 867 | // Technically, we don't have to check anything, but sum(groups) < super | 
|  | 868 | // still applies. | 
|  | 869 | if (!GetVirtualAbFeatureFlag().IsRetrofit()) | 
|  | 870 | return SpaceLimit::ERROR_IF_EXCEEDED_SUPER; | 
|  | 871 |  | 
|  | 872 | // On retrofit VAB device, either of the following: | 
|  | 873 | // - downgrading: allocatable_space = super / 2 | 
|  | 874 | // - secondary payload: don't check anything | 
|  | 875 | // These two cases are indistinguishable, | 
|  | 876 | // hence emit warning if sum(groups) > super / 2 | 
|  | 877 | return SpaceLimit::WARN_IF_EXCEEDED_HALF_OF_SUPER; | 
|  | 878 | } | 
|  | 879 |  | 
| Tianjie | 9f4dc7f | 2021-03-15 16:00:50 -0700 | [diff] [blame] | 880 | bool DynamicPartitionControlAndroid::CheckSuperPartitionAllocatableSpace( | 
|  | 881 | android::fs_mgr::MetadataBuilder* builder, | 
|  | 882 | const DeltaArchiveManifest& manifest, | 
|  | 883 | bool use_snapshot) { | 
| Yifan Hong | 3a1293a | 2021-04-16 13:21:20 -0700 | [diff] [blame] | 884 | uint64_t sum_groups = 0; | 
| Tianjie | 9f4dc7f | 2021-03-15 16:00:50 -0700 | [diff] [blame] | 885 | for (const auto& group : manifest.dynamic_partition_metadata().groups()) { | 
| Yifan Hong | 3a1293a | 2021-04-16 13:21:20 -0700 | [diff] [blame] | 886 | sum_groups += group.size(); | 
| Tianjie | 9f4dc7f | 2021-03-15 16:00:50 -0700 | [diff] [blame] | 887 | } | 
|  | 888 |  | 
| Yifan Hong | 3a1293a | 2021-04-16 13:21:20 -0700 | [diff] [blame] | 889 | uint64_t full_space = builder->AllocatableSpace(); | 
|  | 890 | uint64_t half_space = full_space / 2; | 
|  | 891 | constexpr const char* fmt = | 
|  | 892 | "The maximum size of all groups for the target slot (%" PRIu64 | 
|  | 893 | ") has exceeded %sallocatable space for dynamic partitions %" PRIu64 "."; | 
|  | 894 | switch (GetSpaceLimit(use_snapshot)) { | 
|  | 895 | case SpaceLimit::ERROR_IF_EXCEEDED_HALF_OF_SUPER: { | 
|  | 896 | if (sum_groups > half_space) { | 
|  | 897 | LOG(ERROR) << StringPrintf(fmt, sum_groups, "HALF OF ", half_space); | 
|  | 898 | return false; | 
|  | 899 | } | 
|  | 900 | // If test passes, it implies that the following two conditions also pass. | 
|  | 901 | break; | 
|  | 902 | } | 
|  | 903 | case SpaceLimit::WARN_IF_EXCEEDED_HALF_OF_SUPER: { | 
|  | 904 | if (sum_groups > half_space) { | 
|  | 905 | LOG(WARNING) << StringPrintf(fmt, sum_groups, "HALF OF ", half_space) | 
|  | 906 | << " This is allowed for downgrade or secondary OTA on " | 
|  | 907 | "retrofit VAB device."; | 
|  | 908 | } | 
|  | 909 | // still check sum(groups) < super | 
|  | 910 | [[fallthrough]]; | 
|  | 911 | } | 
|  | 912 | case SpaceLimit::ERROR_IF_EXCEEDED_SUPER: { | 
|  | 913 | if (sum_groups > full_space) { | 
|  | 914 | LOG(ERROR) << base::StringPrintf(fmt, sum_groups, "", full_space); | 
|  | 915 | return false; | 
|  | 916 | } | 
|  | 917 | break; | 
|  | 918 | } | 
| Tianjie | 9f4dc7f | 2021-03-15 16:00:50 -0700 | [diff] [blame] | 919 | } | 
|  | 920 |  | 
|  | 921 | return true; | 
|  | 922 | } | 
|  | 923 |  | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 924 | bool DynamicPartitionControlAndroid::PrepareSnapshotPartitionsForUpdate( | 
|  | 925 | uint32_t source_slot, | 
|  | 926 | uint32_t target_slot, | 
| Yifan Hong | f033ecb | 2020-01-07 18:13:56 -0800 | [diff] [blame] | 927 | const DeltaArchiveManifest& manifest, | 
|  | 928 | uint64_t* required_size) { | 
| Yifan Hong | 4d7c5eb | 2020-04-03 11:31:50 -0700 | [diff] [blame] | 929 | TEST_AND_RETURN_FALSE(ExpectMetadataMounted()); | 
| Tianjie | 9f4dc7f | 2021-03-15 16:00:50 -0700 | [diff] [blame] | 930 |  | 
|  | 931 | std::string device_dir_str; | 
|  | 932 | TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str)); | 
|  | 933 | base::FilePath device_dir(device_dir_str); | 
|  | 934 | auto super_device = | 
|  | 935 | device_dir.Append(GetSuperPartitionName(source_slot)).value(); | 
|  | 936 | auto builder = LoadMetadataBuilder(super_device, source_slot); | 
|  | 937 | if (builder == nullptr) { | 
|  | 938 | LOG(ERROR) << "No metadata at " | 
|  | 939 | << BootControlInterface::SlotName(source_slot); | 
|  | 940 | return false; | 
|  | 941 | } | 
|  | 942 |  | 
|  | 943 | TEST_AND_RETURN_FALSE( | 
|  | 944 | CheckSuperPartitionAllocatableSpace(builder.get(), manifest, true)); | 
|  | 945 |  | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 946 | if (!snapshot_->BeginUpdate()) { | 
|  | 947 | LOG(ERROR) << "Cannot begin new update."; | 
|  | 948 | return false; | 
|  | 949 | } | 
| Yifan Hong | f033ecb | 2020-01-07 18:13:56 -0800 | [diff] [blame] | 950 | auto ret = snapshot_->CreateUpdateSnapshots(manifest); | 
|  | 951 | if (!ret) { | 
|  | 952 | LOG(ERROR) << "Cannot create update snapshots: " << ret.string(); | 
|  | 953 | if (required_size != nullptr && | 
| Yifan Hong | 0850bca | 2020-01-16 15:14:07 -0800 | [diff] [blame] | 954 | ret.error_code() == Return::ErrorCode::NO_SPACE) { | 
| Yifan Hong | f033ecb | 2020-01-07 18:13:56 -0800 | [diff] [blame] | 955 | *required_size = ret.required_size(); | 
|  | 956 | } | 
| Yifan Hong | 420db9b | 2019-07-23 20:50:33 -0700 | [diff] [blame] | 957 | return false; | 
|  | 958 | } | 
|  | 959 | return true; | 
|  | 960 | } | 
|  | 961 |  | 
| Yifan Hong | 700d7c1 | 2019-07-23 20:49:16 -0700 | [diff] [blame] | 962 | std::string DynamicPartitionControlAndroid::GetSuperPartitionName( | 
|  | 963 | uint32_t slot) { | 
|  | 964 | return fs_mgr_get_super_partition_name(slot); | 
|  | 965 | } | 
|  | 966 |  | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 967 | bool DynamicPartitionControlAndroid::UpdatePartitionMetadata( | 
|  | 968 | MetadataBuilder* builder, | 
|  | 969 | uint32_t target_slot, | 
| Yifan Hong | 13d41cb | 2019-09-16 13:18:22 -0700 | [diff] [blame] | 970 | const DeltaArchiveManifest& manifest) { | 
| Yifan Hong | 8d6df9a | 2020-08-13 13:59:54 -0700 | [diff] [blame] | 971 | // Check preconditions. | 
| Yifan Hong | 265a8e3 | 2021-04-16 13:30:02 -0700 | [diff] [blame] | 972 | if (GetVirtualAbFeatureFlag().IsEnabled()) { | 
|  | 973 | CHECK(!target_supports_snapshot_ || IsRecovery()) | 
|  | 974 | << "Must use snapshot on VAB device when target build supports VAB and " | 
|  | 975 | "not sideloading."; | 
|  | 976 | LOG_IF(INFO, !target_supports_snapshot_) | 
|  | 977 | << "Not using snapshot on VAB device because target build does not " | 
|  | 978 | "support snapshot. Secondary or downgrade OTA?"; | 
|  | 979 | LOG_IF(INFO, IsRecovery()) | 
|  | 980 | << "Not using snapshot on VAB device because sideloading."; | 
|  | 981 | } | 
| Yifan Hong | 8d6df9a | 2020-08-13 13:59:54 -0700 | [diff] [blame] | 982 |  | 
| Yifan Hong | a4e7da3 | 2019-09-30 18:25:03 -0700 | [diff] [blame] | 983 | // If applying downgrade from Virtual A/B to non-Virtual A/B, the left-over | 
|  | 984 | // COW group needs to be deleted to ensure there are enough space to create | 
|  | 985 | // target partitions. | 
|  | 986 | builder->RemoveGroupAndPartitions(android::snapshot::kCowGroupName); | 
|  | 987 |  | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 988 | const std::string target_suffix = SlotSuffixForSlotNumber(target_slot); | 
|  | 989 | DeleteGroupsWithSuffix(builder, target_suffix); | 
|  | 990 |  | 
| Tianjie | 9f4dc7f | 2021-03-15 16:00:50 -0700 | [diff] [blame] | 991 | TEST_AND_RETURN_FALSE( | 
|  | 992 | CheckSuperPartitionAllocatableSpace(builder, manifest, false)); | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 993 |  | 
| Yifan Hong | 13d41cb | 2019-09-16 13:18:22 -0700 | [diff] [blame] | 994 | // name of partition(e.g. "system") -> size in bytes | 
|  | 995 | std::map<std::string, uint64_t> partition_sizes; | 
|  | 996 | for (const auto& partition : manifest.partitions()) { | 
|  | 997 | partition_sizes.emplace(partition.partition_name(), | 
|  | 998 | partition.new_partition_info().size()); | 
|  | 999 | } | 
|  | 1000 |  | 
|  | 1001 | for (const auto& group : manifest.dynamic_partition_metadata().groups()) { | 
|  | 1002 | auto group_name_suffix = group.name() + target_suffix; | 
|  | 1003 | if (!builder->AddGroup(group_name_suffix, group.size())) { | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 1004 | LOG(ERROR) << "Cannot add group " << group_name_suffix << " with size " | 
| Yifan Hong | 13d41cb | 2019-09-16 13:18:22 -0700 | [diff] [blame] | 1005 | << group.size(); | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 1006 | return false; | 
|  | 1007 | } | 
|  | 1008 | LOG(INFO) << "Added group " << group_name_suffix << " with size " | 
| Yifan Hong | 13d41cb | 2019-09-16 13:18:22 -0700 | [diff] [blame] | 1009 | << group.size(); | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 1010 |  | 
| Yifan Hong | 13d41cb | 2019-09-16 13:18:22 -0700 | [diff] [blame] | 1011 | for (const auto& partition_name : group.partition_names()) { | 
|  | 1012 | auto partition_sizes_it = partition_sizes.find(partition_name); | 
|  | 1013 | if (partition_sizes_it == partition_sizes.end()) { | 
|  | 1014 | // TODO(tbao): Support auto-filling partition info for framework-only | 
|  | 1015 | // OTA. | 
|  | 1016 | LOG(ERROR) << "dynamic_partition_metadata contains partition " | 
|  | 1017 | << partition_name << " but it is not part of the manifest. " | 
|  | 1018 | << "This is not supported."; | 
|  | 1019 | return false; | 
|  | 1020 | } | 
|  | 1021 | uint64_t partition_size = partition_sizes_it->second; | 
|  | 1022 |  | 
|  | 1023 | auto partition_name_suffix = partition_name + target_suffix; | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 1024 | Partition* p = builder->AddPartition( | 
|  | 1025 | partition_name_suffix, group_name_suffix, LP_PARTITION_ATTR_READONLY); | 
|  | 1026 | if (!p) { | 
|  | 1027 | LOG(ERROR) << "Cannot add partition " << partition_name_suffix | 
|  | 1028 | << " to group " << group_name_suffix; | 
|  | 1029 | return false; | 
|  | 1030 | } | 
| Yifan Hong | 13d41cb | 2019-09-16 13:18:22 -0700 | [diff] [blame] | 1031 | if (!builder->ResizePartition(p, partition_size)) { | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 1032 | LOG(ERROR) << "Cannot resize partition " << partition_name_suffix | 
| Yifan Hong | 13d41cb | 2019-09-16 13:18:22 -0700 | [diff] [blame] | 1033 | << " to size " << partition_size << ". Not enough space?"; | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 1034 | return false; | 
|  | 1035 | } | 
| David Anderson | 0e1c7fd | 2021-03-08 19:01:59 -0800 | [diff] [blame] | 1036 | if (p->size() < partition_size) { | 
|  | 1037 | LOG(ERROR) << "Partition " << partition_name_suffix | 
|  | 1038 | << " was expected to have size " << partition_size | 
|  | 1039 | << ", but instead has size " << p->size(); | 
|  | 1040 | return false; | 
|  | 1041 | } | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 1042 | LOG(INFO) << "Added partition " << partition_name_suffix << " to group " | 
| Yifan Hong | 13d41cb | 2019-09-16 13:18:22 -0700 | [diff] [blame] | 1043 | << group_name_suffix << " with size " << partition_size; | 
| Yifan Hong | 012508e | 2019-07-22 18:30:40 -0700 | [diff] [blame] | 1044 | } | 
|  | 1045 | } | 
|  | 1046 |  | 
|  | 1047 | return true; | 
|  | 1048 | } | 
|  | 1049 |  | 
| Yifan Hong | 7b3910a | 2020-03-24 17:47:32 -0700 | [diff] [blame] | 1050 | bool DynamicPartitionControlAndroid::FinishUpdate(bool powerwash_required) { | 
| Yifan Hong | 4d7c5eb | 2020-04-03 11:31:50 -0700 | [diff] [blame] | 1051 | if (ExpectMetadataMounted()) { | 
|  | 1052 | if (snapshot_->GetUpdateState() == UpdateState::Initiated) { | 
|  | 1053 | LOG(INFO) << "Snapshot writes are done."; | 
|  | 1054 | return snapshot_->FinishedSnapshotWrites(powerwash_required); | 
|  | 1055 | } | 
|  | 1056 | } else { | 
|  | 1057 | LOG(INFO) << "Skip FinishedSnapshotWrites() because /metadata is not " | 
|  | 1058 | << "mounted"; | 
| Yifan Hong | f0f4a91 | 2019-09-26 17:51:33 -0700 | [diff] [blame] | 1059 | } | 
|  | 1060 | return true; | 
| Yifan Hong | a33bca4 | 2019-09-03 20:29:45 -0700 | [diff] [blame] | 1061 | } | 
|  | 1062 |  | 
| Yifan Hong | 3a1a561 | 2019-11-05 16:34:32 -0800 | [diff] [blame] | 1063 | bool DynamicPartitionControlAndroid::GetPartitionDevice( | 
|  | 1064 | const std::string& partition_name, | 
|  | 1065 | uint32_t slot, | 
|  | 1066 | uint32_t current_slot, | 
| Tianjie | 51a5a39 | 2020-06-03 14:39:32 -0700 | [diff] [blame] | 1067 | bool not_in_payload, | 
|  | 1068 | std::string* device, | 
|  | 1069 | bool* is_dynamic) { | 
| Kelvin Zhang | 66a9ebb | 2021-01-25 13:35:10 -0500 | [diff] [blame] | 1070 | auto partition_dev = | 
|  | 1071 | GetPartitionDevice(partition_name, slot, current_slot, not_in_payload); | 
| Kelvin Zhang | 91d95fa | 2020-11-05 13:52:00 -0500 | [diff] [blame] | 1072 | if (!partition_dev.has_value()) { | 
| Yifan Hong | 3a1a561 | 2019-11-05 16:34:32 -0800 | [diff] [blame] | 1073 | return false; | 
|  | 1074 | } | 
| Kelvin Zhang | 91d95fa | 2020-11-05 13:52:00 -0500 | [diff] [blame] | 1075 | if (device) { | 
|  | 1076 | *device = std::move(partition_dev->rw_device_path); | 
|  | 1077 | } | 
|  | 1078 | if (is_dynamic) { | 
|  | 1079 | *is_dynamic = partition_dev->is_dynamic; | 
|  | 1080 | } | 
| Yifan Hong | 3a1a561 | 2019-11-05 16:34:32 -0800 | [diff] [blame] | 1081 | return true; | 
|  | 1082 | } | 
|  | 1083 |  | 
| Tianjie | 51a5a39 | 2020-06-03 14:39:32 -0700 | [diff] [blame] | 1084 | bool DynamicPartitionControlAndroid::GetPartitionDevice( | 
|  | 1085 | const std::string& partition_name, | 
|  | 1086 | uint32_t slot, | 
|  | 1087 | uint32_t current_slot, | 
|  | 1088 | std::string* device) { | 
|  | 1089 | return GetPartitionDevice( | 
|  | 1090 | partition_name, slot, current_slot, false, device, nullptr); | 
|  | 1091 | } | 
|  | 1092 |  | 
| Kelvin Zhang | 91d95fa | 2020-11-05 13:52:00 -0500 | [diff] [blame] | 1093 | static std::string GetStaticDevicePath( | 
|  | 1094 | const base::FilePath& device_dir, | 
|  | 1095 | const std::string& partition_name_suffixed) { | 
|  | 1096 | base::FilePath path = device_dir.Append(partition_name_suffixed); | 
|  | 1097 | return path.value(); | 
|  | 1098 | } | 
|  | 1099 |  | 
|  | 1100 | std::optional<PartitionDevice> | 
|  | 1101 | DynamicPartitionControlAndroid::GetPartitionDevice( | 
|  | 1102 | const std::string& partition_name, | 
|  | 1103 | uint32_t slot, | 
|  | 1104 | uint32_t current_slot, | 
|  | 1105 | bool not_in_payload) { | 
|  | 1106 | std::string device_dir_str; | 
|  | 1107 | if (!GetDeviceDir(&device_dir_str)) { | 
|  | 1108 | LOG(ERROR) << "Failed to GetDeviceDir()"; | 
|  | 1109 | return {}; | 
|  | 1110 | } | 
|  | 1111 | const base::FilePath device_dir(device_dir_str); | 
|  | 1112 | // When VABC is enabled, we can't get device path for dynamic partitions in | 
|  | 1113 | // target slot. | 
|  | 1114 | const auto& partition_name_suffix = | 
|  | 1115 | partition_name + SlotSuffixForSlotNumber(slot); | 
| Kelvin Zhang | ebd115e | 2021-03-08 16:10:25 -0500 | [diff] [blame] | 1116 | if (UpdateUsesSnapshotCompression() && slot != current_slot && | 
|  | 1117 | IsDynamicPartition(partition_name, slot)) { | 
| Kelvin Zhang | 91ad662 | 2021-03-01 13:46:17 -0500 | [diff] [blame] | 1118 | return { | 
| Kelvin Zhang | a9b5d8c | 2021-05-05 09:17:46 -0400 | [diff] [blame] | 1119 | {.readonly_device_path = base::FilePath{std::string{VABC_DEVICE_DIR}} | 
|  | 1120 | .Append(partition_name_suffix) | 
|  | 1121 | .value(), | 
| Kelvin Zhang | 91ad662 | 2021-03-01 13:46:17 -0500 | [diff] [blame] | 1122 | .is_dynamic = true}}; | 
| Kelvin Zhang | 91d95fa | 2020-11-05 13:52:00 -0500 | [diff] [blame] | 1123 | } | 
|  | 1124 |  | 
|  | 1125 | // When looking up target partition devices, treat them as static if the | 
|  | 1126 | // current payload doesn't encode them as dynamic partitions. This may happen | 
|  | 1127 | // when applying a retrofit update on top of a dynamic-partitions-enabled | 
|  | 1128 | // build. | 
|  | 1129 | std::string device; | 
|  | 1130 | if (GetDynamicPartitionsFeatureFlag().IsEnabled() && | 
|  | 1131 | (slot == current_slot || is_target_dynamic_)) { | 
|  | 1132 | switch (GetDynamicPartitionDevice(device_dir, | 
|  | 1133 | partition_name_suffix, | 
|  | 1134 | slot, | 
|  | 1135 | current_slot, | 
|  | 1136 | not_in_payload, | 
|  | 1137 | &device)) { | 
|  | 1138 | case DynamicPartitionDeviceStatus::SUCCESS: | 
|  | 1139 | return {{.rw_device_path = device, | 
| Kelvin Zhang | a9b5d8c | 2021-05-05 09:17:46 -0400 | [diff] [blame] | 1140 | .readonly_device_path = device, | 
| Kelvin Zhang | 91d95fa | 2020-11-05 13:52:00 -0500 | [diff] [blame] | 1141 | .is_dynamic = true}}; | 
|  | 1142 |  | 
|  | 1143 | case DynamicPartitionDeviceStatus::TRY_STATIC: | 
|  | 1144 | break; | 
|  | 1145 | case DynamicPartitionDeviceStatus::ERROR:  // fallthrough | 
|  | 1146 | default: | 
|  | 1147 | return {}; | 
|  | 1148 | } | 
|  | 1149 | } | 
|  | 1150 | // Try static partitions. | 
|  | 1151 | auto static_path = GetStaticDevicePath(device_dir, partition_name_suffix); | 
|  | 1152 | if (!DeviceExists(static_path)) { | 
|  | 1153 | LOG(ERROR) << "Device file " << static_path << " does not exist."; | 
|  | 1154 | return {}; | 
|  | 1155 | } | 
|  | 1156 |  | 
|  | 1157 | return {{.rw_device_path = static_path, | 
| Kelvin Zhang | a9b5d8c | 2021-05-05 09:17:46 -0400 | [diff] [blame] | 1158 | .readonly_device_path = static_path, | 
| Kelvin Zhang | 91d95fa | 2020-11-05 13:52:00 -0500 | [diff] [blame] | 1159 | .is_dynamic = false}}; | 
|  | 1160 | } | 
|  | 1161 |  | 
| Yifan Hong | 3a1a561 | 2019-11-05 16:34:32 -0800 | [diff] [blame] | 1162 | bool DynamicPartitionControlAndroid::IsSuperBlockDevice( | 
|  | 1163 | const base::FilePath& device_dir, | 
|  | 1164 | uint32_t current_slot, | 
|  | 1165 | const std::string& partition_name_suffix) { | 
|  | 1166 | std::string source_device = | 
|  | 1167 | device_dir.Append(GetSuperPartitionName(current_slot)).value(); | 
|  | 1168 | auto source_metadata = LoadMetadataBuilder(source_device, current_slot); | 
|  | 1169 | return source_metadata->HasBlockDevice(partition_name_suffix); | 
|  | 1170 | } | 
|  | 1171 |  | 
|  | 1172 | DynamicPartitionControlAndroid::DynamicPartitionDeviceStatus | 
|  | 1173 | DynamicPartitionControlAndroid::GetDynamicPartitionDevice( | 
|  | 1174 | const base::FilePath& device_dir, | 
|  | 1175 | const std::string& partition_name_suffix, | 
|  | 1176 | uint32_t slot, | 
|  | 1177 | uint32_t current_slot, | 
| Tianjie | 51a5a39 | 2020-06-03 14:39:32 -0700 | [diff] [blame] | 1178 | bool not_in_payload, | 
| Yifan Hong | 3a1a561 | 2019-11-05 16:34:32 -0800 | [diff] [blame] | 1179 | std::string* device) { | 
|  | 1180 | std::string super_device = | 
|  | 1181 | device_dir.Append(GetSuperPartitionName(slot)).value(); | 
|  | 1182 |  | 
|  | 1183 | auto builder = LoadMetadataBuilder(super_device, slot); | 
|  | 1184 | if (builder == nullptr) { | 
|  | 1185 | LOG(ERROR) << "No metadata in slot " | 
|  | 1186 | << BootControlInterface::SlotName(slot); | 
|  | 1187 | return DynamicPartitionDeviceStatus::ERROR; | 
|  | 1188 | } | 
|  | 1189 | if (builder->FindPartition(partition_name_suffix) == nullptr) { | 
|  | 1190 | LOG(INFO) << partition_name_suffix | 
|  | 1191 | << " is not in super partition metadata."; | 
|  | 1192 |  | 
|  | 1193 | if (IsSuperBlockDevice(device_dir, current_slot, partition_name_suffix)) { | 
|  | 1194 | LOG(ERROR) << "The static partition " << partition_name_suffix | 
|  | 1195 | << " is a block device for current metadata." | 
|  | 1196 | << "It cannot be used as a logical partition."; | 
|  | 1197 | return DynamicPartitionDeviceStatus::ERROR; | 
|  | 1198 | } | 
|  | 1199 |  | 
|  | 1200 | return DynamicPartitionDeviceStatus::TRY_STATIC; | 
|  | 1201 | } | 
|  | 1202 |  | 
|  | 1203 | if (slot == current_slot) { | 
|  | 1204 | if (GetState(partition_name_suffix) != DmDeviceState::ACTIVE) { | 
|  | 1205 | LOG(WARNING) << partition_name_suffix << " is at current slot but it is " | 
|  | 1206 | << "not mapped. Now try to map it."; | 
|  | 1207 | } else { | 
|  | 1208 | if (GetDmDevicePathByName(partition_name_suffix, device)) { | 
|  | 1209 | LOG(INFO) << partition_name_suffix | 
|  | 1210 | << " is mapped on device mapper: " << *device; | 
|  | 1211 | return DynamicPartitionDeviceStatus::SUCCESS; | 
|  | 1212 | } | 
|  | 1213 | LOG(ERROR) << partition_name_suffix << "is mapped but path is unknown."; | 
|  | 1214 | return DynamicPartitionDeviceStatus::ERROR; | 
|  | 1215 | } | 
|  | 1216 | } | 
|  | 1217 |  | 
| Tianjie | 51a5a39 | 2020-06-03 14:39:32 -0700 | [diff] [blame] | 1218 | bool force_writable = (slot != current_slot) && !not_in_payload; | 
| Yifan Hong | 3a1a561 | 2019-11-05 16:34:32 -0800 | [diff] [blame] | 1219 | if (MapPartitionOnDeviceMapper( | 
|  | 1220 | super_device, partition_name_suffix, slot, force_writable, device)) { | 
|  | 1221 | return DynamicPartitionDeviceStatus::SUCCESS; | 
|  | 1222 | } | 
|  | 1223 | return DynamicPartitionDeviceStatus::ERROR; | 
|  | 1224 | } | 
|  | 1225 |  | 
| Yifan Hong | 6eec995 | 2019-12-04 13:12:01 -0800 | [diff] [blame] | 1226 | void DynamicPartitionControlAndroid::set_fake_mapped_devices( | 
|  | 1227 | const std::set<std::string>& fake) { | 
|  | 1228 | mapped_devices_ = fake; | 
|  | 1229 | } | 
|  | 1230 |  | 
| Yifan Hong | bae2784 | 2019-10-24 16:56:12 -0700 | [diff] [blame] | 1231 | bool DynamicPartitionControlAndroid::IsRecovery() { | 
| Kelvin Zhang | a22ef55 | 2020-10-12 19:03:52 -0400 | [diff] [blame] | 1232 | return constants::kIsRecovery; | 
| Yifan Hong | bae2784 | 2019-10-24 16:56:12 -0700 | [diff] [blame] | 1233 | } | 
|  | 1234 |  | 
|  | 1235 | static bool IsIncrementalUpdate(const DeltaArchiveManifest& manifest) { | 
|  | 1236 | const auto& partitions = manifest.partitions(); | 
|  | 1237 | return std::any_of(partitions.begin(), partitions.end(), [](const auto& p) { | 
|  | 1238 | return p.has_old_partition_info(); | 
|  | 1239 | }); | 
|  | 1240 | } | 
|  | 1241 |  | 
|  | 1242 | bool DynamicPartitionControlAndroid::DeleteSourcePartitions( | 
|  | 1243 | MetadataBuilder* builder, | 
|  | 1244 | uint32_t source_slot, | 
|  | 1245 | const DeltaArchiveManifest& manifest) { | 
|  | 1246 | TEST_AND_RETURN_FALSE(IsRecovery()); | 
|  | 1247 |  | 
|  | 1248 | if (IsIncrementalUpdate(manifest)) { | 
|  | 1249 | LOG(ERROR) << "Cannot sideload incremental OTA because snapshots cannot " | 
|  | 1250 | << "be created."; | 
|  | 1251 | if (GetVirtualAbFeatureFlag().IsLaunch()) { | 
|  | 1252 | LOG(ERROR) << "Sideloading incremental updates on devices launches " | 
|  | 1253 | << " Virtual A/B is not supported."; | 
|  | 1254 | } | 
|  | 1255 | return false; | 
|  | 1256 | } | 
|  | 1257 |  | 
|  | 1258 | LOG(INFO) << "Will overwrite existing partitions. Slot " | 
|  | 1259 | << BootControlInterface::SlotName(source_slot) | 
| Tianjie | 9f4dc7f | 2021-03-15 16:00:50 -0700 | [diff] [blame] | 1260 | << " may be unbootable until update finishes!"; | 
| Yifan Hong | bae2784 | 2019-10-24 16:56:12 -0700 | [diff] [blame] | 1261 | const std::string source_suffix = SlotSuffixForSlotNumber(source_slot); | 
|  | 1262 | DeleteGroupsWithSuffix(builder, source_suffix); | 
|  | 1263 |  | 
|  | 1264 | return true; | 
|  | 1265 | } | 
|  | 1266 |  | 
| Yifan Hong | 9096550 | 2020-02-19 15:22:47 -0800 | [diff] [blame] | 1267 | std::unique_ptr<AbstractAction> | 
|  | 1268 | DynamicPartitionControlAndroid::GetCleanupPreviousUpdateAction( | 
|  | 1269 | BootControlInterface* boot_control, | 
|  | 1270 | PrefsInterface* prefs, | 
|  | 1271 | CleanupPreviousUpdateActionDelegateInterface* delegate) { | 
|  | 1272 | if (!GetVirtualAbFeatureFlag().IsEnabled()) { | 
|  | 1273 | return std::make_unique<NoOpAction>(); | 
|  | 1274 | } | 
|  | 1275 | return std::make_unique<CleanupPreviousUpdateAction>( | 
|  | 1276 | prefs, boot_control, snapshot_.get(), delegate); | 
|  | 1277 | } | 
|  | 1278 |  | 
| Yifan Hong | 6a6d0f1 | 2020-03-11 13:20:52 -0700 | [diff] [blame] | 1279 | bool DynamicPartitionControlAndroid::ResetUpdate(PrefsInterface* prefs) { | 
|  | 1280 | if (!GetVirtualAbFeatureFlag().IsEnabled()) { | 
|  | 1281 | return true; | 
|  | 1282 | } | 
|  | 1283 |  | 
|  | 1284 | LOG(INFO) << __func__ << " resetting update state and deleting snapshots."; | 
|  | 1285 | TEST_AND_RETURN_FALSE(prefs != nullptr); | 
|  | 1286 |  | 
|  | 1287 | // If the device has already booted into the target slot, | 
|  | 1288 | // ResetUpdateProgress may pass but CancelUpdate fails. | 
|  | 1289 | // This is expected. A scheduled CleanupPreviousUpdateAction should free | 
|  | 1290 | // space when it is done. | 
|  | 1291 | TEST_AND_RETURN_FALSE(DeltaPerformer::ResetUpdateProgress( | 
|  | 1292 | prefs, false /* quick */, false /* skip dynamic partitions metadata */)); | 
|  | 1293 |  | 
| Yifan Hong | 4d7c5eb | 2020-04-03 11:31:50 -0700 | [diff] [blame] | 1294 | if (ExpectMetadataMounted()) { | 
|  | 1295 | TEST_AND_RETURN_FALSE(snapshot_->CancelUpdate()); | 
|  | 1296 | } else { | 
|  | 1297 | LOG(INFO) << "Skip cancelling update in ResetUpdate because /metadata is " | 
|  | 1298 | << "not mounted"; | 
|  | 1299 | } | 
| Yifan Hong | 6a6d0f1 | 2020-03-11 13:20:52 -0700 | [diff] [blame] | 1300 |  | 
|  | 1301 | return true; | 
|  | 1302 | } | 
|  | 1303 |  | 
| Tianjie | 99d570d | 2020-06-04 14:57:19 -0700 | [diff] [blame] | 1304 | bool DynamicPartitionControlAndroid::ListDynamicPartitionsForSlot( | 
| Tianjie | 3a55fc2 | 2021-02-13 16:02:22 -0800 | [diff] [blame] | 1305 | uint32_t slot, | 
|  | 1306 | uint32_t current_slot, | 
|  | 1307 | std::vector<std::string>* partitions) { | 
| Kelvin Zhang | ebd115e | 2021-03-08 16:10:25 -0500 | [diff] [blame] | 1308 | CHECK(slot == source_slot_ || target_slot_ != UINT32_MAX) | 
|  | 1309 | << " source slot: " << source_slot_ << " target slot: " << target_slot_ | 
|  | 1310 | << " slot: " << slot | 
|  | 1311 | << " attempting to query dynamic partition metadata for target slot " | 
|  | 1312 | "before PreparePartitionForUpdate() is called. The " | 
|  | 1313 | "metadata in target slot isn't valid until " | 
|  | 1314 | "PreparePartitionForUpdate() is called, contining execution would " | 
|  | 1315 | "likely cause problems."; | 
| Tianjie | 3a55fc2 | 2021-02-13 16:02:22 -0800 | [diff] [blame] | 1316 | bool slot_enables_dynamic_partitions = | 
|  | 1317 | GetDynamicPartitionsFeatureFlag().IsEnabled(); | 
|  | 1318 | // Check if the target slot has dynamic partitions, this may happen when | 
|  | 1319 | // applying a retrofit package. | 
|  | 1320 | if (slot != current_slot) { | 
|  | 1321 | slot_enables_dynamic_partitions = | 
|  | 1322 | slot_enables_dynamic_partitions && is_target_dynamic_; | 
|  | 1323 | } | 
|  | 1324 |  | 
|  | 1325 | if (!slot_enables_dynamic_partitions) { | 
|  | 1326 | LOG(INFO) << "Dynamic partition is not enabled for slot " << slot; | 
|  | 1327 | return true; | 
| Tianjie | 99d570d | 2020-06-04 14:57:19 -0700 | [diff] [blame] | 1328 | } | 
|  | 1329 |  | 
|  | 1330 | std::string device_dir_str; | 
|  | 1331 | TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str)); | 
|  | 1332 | base::FilePath device_dir(device_dir_str); | 
| Tianjie | 3a55fc2 | 2021-02-13 16:02:22 -0800 | [diff] [blame] | 1333 | auto super_device = device_dir.Append(GetSuperPartitionName(slot)).value(); | 
|  | 1334 | auto builder = LoadMetadataBuilder(super_device, slot); | 
| Tianjie | 99d570d | 2020-06-04 14:57:19 -0700 | [diff] [blame] | 1335 | TEST_AND_RETURN_FALSE(builder != nullptr); | 
|  | 1336 |  | 
|  | 1337 | std::vector<std::string> result; | 
| Tianjie | 3a55fc2 | 2021-02-13 16:02:22 -0800 | [diff] [blame] | 1338 | auto suffix = SlotSuffixForSlotNumber(slot); | 
| Tianjie | 99d570d | 2020-06-04 14:57:19 -0700 | [diff] [blame] | 1339 | for (const auto& group : builder->ListGroups()) { | 
|  | 1340 | for (const auto& partition : builder->ListPartitionsInGroup(group)) { | 
|  | 1341 | std::string_view partition_name = partition->name(); | 
|  | 1342 | if (!android::base::ConsumeSuffix(&partition_name, suffix)) { | 
|  | 1343 | continue; | 
|  | 1344 | } | 
|  | 1345 | result.emplace_back(partition_name); | 
|  | 1346 | } | 
|  | 1347 | } | 
|  | 1348 | *partitions = std::move(result); | 
|  | 1349 | return true; | 
|  | 1350 | } | 
|  | 1351 |  | 
| Tianjie | 24f9609 | 2020-06-30 12:26:25 -0700 | [diff] [blame] | 1352 | bool DynamicPartitionControlAndroid::VerifyExtentsForUntouchedPartitions( | 
|  | 1353 | uint32_t source_slot, | 
|  | 1354 | uint32_t target_slot, | 
|  | 1355 | const std::vector<std::string>& partitions) { | 
|  | 1356 | std::string device_dir_str; | 
|  | 1357 | TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str)); | 
|  | 1358 | base::FilePath device_dir(device_dir_str); | 
|  | 1359 |  | 
|  | 1360 | auto source_super_device = | 
|  | 1361 | device_dir.Append(GetSuperPartitionName(source_slot)).value(); | 
|  | 1362 | auto source_builder = LoadMetadataBuilder(source_super_device, source_slot); | 
|  | 1363 | TEST_AND_RETURN_FALSE(source_builder != nullptr); | 
|  | 1364 |  | 
|  | 1365 | auto target_super_device = | 
|  | 1366 | device_dir.Append(GetSuperPartitionName(target_slot)).value(); | 
|  | 1367 | auto target_builder = LoadMetadataBuilder(target_super_device, target_slot); | 
|  | 1368 | TEST_AND_RETURN_FALSE(target_builder != nullptr); | 
|  | 1369 |  | 
|  | 1370 | return MetadataBuilder::VerifyExtentsAgainstSourceMetadata( | 
|  | 1371 | *source_builder, source_slot, *target_builder, target_slot, partitions); | 
|  | 1372 | } | 
|  | 1373 |  | 
| Yifan Hong | 4d7c5eb | 2020-04-03 11:31:50 -0700 | [diff] [blame] | 1374 | bool DynamicPartitionControlAndroid::ExpectMetadataMounted() { | 
|  | 1375 | // No need to mount metadata for non-Virtual A/B devices. | 
|  | 1376 | if (!GetVirtualAbFeatureFlag().IsEnabled()) { | 
|  | 1377 | return false; | 
|  | 1378 | } | 
|  | 1379 | // Intentionally not checking |metadata_device_| in Android mode. | 
|  | 1380 | // /metadata should always be mounted in Android mode. If it isn't, let caller | 
|  | 1381 | // fails when calling into SnapshotManager. | 
|  | 1382 | if (!IsRecovery()) { | 
|  | 1383 | return true; | 
|  | 1384 | } | 
|  | 1385 | // In recovery mode, explicitly check |metadata_device_|. | 
|  | 1386 | return metadata_device_ != nullptr; | 
|  | 1387 | } | 
|  | 1388 |  | 
|  | 1389 | bool DynamicPartitionControlAndroid::EnsureMetadataMounted() { | 
|  | 1390 | // No need to mount metadata for non-Virtual A/B devices. | 
|  | 1391 | if (!GetVirtualAbFeatureFlag().IsEnabled()) { | 
|  | 1392 | return true; | 
|  | 1393 | } | 
|  | 1394 |  | 
|  | 1395 | if (metadata_device_ == nullptr) { | 
|  | 1396 | metadata_device_ = snapshot_->EnsureMetadataMounted(); | 
|  | 1397 | } | 
|  | 1398 | return metadata_device_ != nullptr; | 
|  | 1399 | } | 
|  | 1400 |  | 
| Kelvin Zhang | 3461852 | 2020-09-28 09:21:02 -0400 | [diff] [blame] | 1401 | std::unique_ptr<android::snapshot::ISnapshotWriter> | 
|  | 1402 | DynamicPartitionControlAndroid::OpenCowWriter( | 
|  | 1403 | const std::string& partition_name, | 
|  | 1404 | const std::optional<std::string>& source_path, | 
|  | 1405 | bool is_append) { | 
|  | 1406 | auto suffix = SlotSuffixForSlotNumber(target_slot_); | 
|  | 1407 |  | 
| Kelvin Zhang | 9d87d6d | 2020-10-23 17:03:59 -0400 | [diff] [blame] | 1408 | auto super_device = GetSuperDevice(); | 
|  | 1409 | if (!super_device.has_value()) { | 
| Kelvin Zhang | 3461852 | 2020-09-28 09:21:02 -0400 | [diff] [blame] | 1410 | return nullptr; | 
|  | 1411 | } | 
| Kelvin Zhang | 3461852 | 2020-09-28 09:21:02 -0400 | [diff] [blame] | 1412 | CreateLogicalPartitionParams params = { | 
| Kelvin Zhang | 9d87d6d | 2020-10-23 17:03:59 -0400 | [diff] [blame] | 1413 | .block_device = super_device->value(), | 
| Kelvin Zhang | 3461852 | 2020-09-28 09:21:02 -0400 | [diff] [blame] | 1414 | .metadata_slot = target_slot_, | 
|  | 1415 | .partition_name = partition_name + suffix, | 
|  | 1416 | .force_writable = true, | 
| Kelvin Zhang | 877ddbe | 2020-11-09 13:37:56 -0500 | [diff] [blame] | 1417 | .timeout_ms = kMapSnapshotTimeout}; | 
| Kelvin Zhang | 3461852 | 2020-09-28 09:21:02 -0400 | [diff] [blame] | 1418 | // TODO(zhangkelvin) Open an APPEND mode CowWriter once there's an API to do | 
|  | 1419 | // it. | 
|  | 1420 | return snapshot_->OpenSnapshotWriter(params, std::move(source_path)); | 
| Kelvin Zhang | 877ddbe | 2020-11-09 13:37:56 -0500 | [diff] [blame] | 1421 | }  // namespace chromeos_update_engine | 
| Kelvin Zhang | 3461852 | 2020-09-28 09:21:02 -0400 | [diff] [blame] | 1422 |  | 
| Kelvin Zhang | 21a4991 | 2021-03-12 14:28:33 -0500 | [diff] [blame] | 1423 | FileDescriptorPtr DynamicPartitionControlAndroid::OpenCowFd( | 
| Kelvin Zhang | c82511c | 2020-11-06 16:01:24 -0500 | [diff] [blame] | 1424 | const std::string& unsuffixed_partition_name, | 
|  | 1425 | const std::optional<std::string>& source_path, | 
|  | 1426 | bool is_append) { | 
|  | 1427 | auto cow_writer = | 
|  | 1428 | OpenCowWriter(unsuffixed_partition_name, source_path, is_append); | 
|  | 1429 | if (cow_writer == nullptr) { | 
|  | 1430 | return nullptr; | 
|  | 1431 | } | 
| Kelvin Zhang | 21a4991 | 2021-03-12 14:28:33 -0500 | [diff] [blame] | 1432 | if (!cow_writer->InitializeAppend(kEndOfInstallLabel)) { | 
|  | 1433 | return nullptr; | 
|  | 1434 | } | 
|  | 1435 | return std::make_shared<CowWriterFileDescriptor>(std::move(cow_writer)); | 
| Kelvin Zhang | c82511c | 2020-11-06 16:01:24 -0500 | [diff] [blame] | 1436 | } | 
|  | 1437 |  | 
| Kelvin Zhang | 9d87d6d | 2020-10-23 17:03:59 -0400 | [diff] [blame] | 1438 | std::optional<base::FilePath> DynamicPartitionControlAndroid::GetSuperDevice() { | 
|  | 1439 | std::string device_dir_str; | 
|  | 1440 | if (!GetDeviceDir(&device_dir_str)) { | 
|  | 1441 | LOG(ERROR) << "Failed to get device dir!"; | 
|  | 1442 | return {}; | 
|  | 1443 | } | 
|  | 1444 | base::FilePath device_dir(device_dir_str); | 
|  | 1445 | auto super_device = device_dir.Append(GetSuperPartitionName(target_slot_)); | 
|  | 1446 | return super_device; | 
|  | 1447 | } | 
|  | 1448 |  | 
|  | 1449 | bool DynamicPartitionControlAndroid::MapAllPartitions() { | 
| Kelvin Zhang | 877ddbe | 2020-11-09 13:37:56 -0500 | [diff] [blame] | 1450 | return snapshot_->MapAllSnapshots(kMapSnapshotTimeout); | 
| Kelvin Zhang | 9d87d6d | 2020-10-23 17:03:59 -0400 | [diff] [blame] | 1451 | } | 
|  | 1452 |  | 
| Kelvin Zhang | eb9de16 | 2020-11-16 15:47:28 -0500 | [diff] [blame] | 1453 | bool DynamicPartitionControlAndroid::IsDynamicPartition( | 
| Kelvin Zhang | ebd115e | 2021-03-08 16:10:25 -0500 | [diff] [blame] | 1454 | const std::string& partition_name, uint32_t slot) { | 
|  | 1455 | if (slot >= dynamic_partition_list_.size()) { | 
|  | 1456 | LOG(ERROR) << "Seeing unexpected slot # " << slot << " currently assuming " | 
|  | 1457 | << dynamic_partition_list_.size() << " slots"; | 
|  | 1458 | return false; | 
|  | 1459 | } | 
|  | 1460 | auto& dynamic_partition_list = dynamic_partition_list_[slot]; | 
|  | 1461 | if (dynamic_partition_list.empty() && | 
| Kelvin Zhang | eb9de16 | 2020-11-16 15:47:28 -0500 | [diff] [blame] | 1462 | GetDynamicPartitionsFeatureFlag().IsEnabled()) { | 
| Tianjie | 3a55fc2 | 2021-02-13 16:02:22 -0800 | [diff] [blame] | 1463 | // Use the DAP config of the target slot. | 
|  | 1464 | CHECK(ListDynamicPartitionsForSlot( | 
| Kelvin Zhang | ebd115e | 2021-03-08 16:10:25 -0500 | [diff] [blame] | 1465 | slot, source_slot_, &dynamic_partition_list)); | 
| Kelvin Zhang | eb9de16 | 2020-11-16 15:47:28 -0500 | [diff] [blame] | 1466 | } | 
| Kelvin Zhang | ebd115e | 2021-03-08 16:10:25 -0500 | [diff] [blame] | 1467 | return std::find(dynamic_partition_list.begin(), | 
|  | 1468 | dynamic_partition_list.end(), | 
|  | 1469 | partition_name) != dynamic_partition_list.end(); | 
| Kelvin Zhang | eb9de16 | 2020-11-16 15:47:28 -0500 | [diff] [blame] | 1470 | } | 
| Kelvin Zhang | 91d95fa | 2020-11-05 13:52:00 -0500 | [diff] [blame] | 1471 |  | 
| Yifan Hong | b0cbd39 | 2021-02-04 11:11:45 -0800 | [diff] [blame] | 1472 | bool DynamicPartitionControlAndroid::UpdateUsesSnapshotCompression() { | 
| Kelvin Zhang | 741c2d4 | 2021-04-13 12:40:38 -0400 | [diff] [blame] | 1473 | return GetVirtualAbFeatureFlag().IsEnabled() && | 
|  | 1474 | snapshot_->UpdateUsesCompression(); | 
| Yifan Hong | b0cbd39 | 2021-02-04 11:11:45 -0800 | [diff] [blame] | 1475 | } | 
|  | 1476 |  | 
| Yifan Hong | 537802d | 2018-08-15 13:15:42 -0700 | [diff] [blame] | 1477 | }  // namespace chromeos_update_engine |