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