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