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