blob: 81d0d770a9986ee6c4a76efef219c7769f094fa9 [file] [log] [blame]
Yifan Hong537802d2018-08-15 13:15:42 -07001//
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 Hong420db9b2019-07-23 20:50:33 -070019#include <chrono> // NOLINT(build/c++11) - using libsnapshot / liblp API
Yifan Hong13d41cb2019-09-16 13:18:22 -070020#include <map>
Yifan Hong537802d2018-08-15 13:15:42 -070021#include <memory>
22#include <set>
23#include <string>
Yifan Hong012508e2019-07-22 18:30:40 -070024#include <vector>
Yifan Hong537802d2018-08-15 13:15:42 -070025
26#include <android-base/properties.h>
27#include <android-base/strings.h>
28#include <base/files/file_util.h>
29#include <base/logging.h>
Yifan Hong012508e2019-07-22 18:30:40 -070030#include <base/strings/string_util.h>
Yifan Hong537802d2018-08-15 13:15:42 -070031#include <bootloader_message/bootloader_message.h>
Yifan Hong012508e2019-07-22 18:30:40 -070032#include <fs_mgr.h>
Yifan Hong537802d2018-08-15 13:15:42 -070033#include <fs_mgr_dm_linear.h>
Yifan Hong3a1a5612019-11-05 16:34:32 -080034#include <fs_mgr_overlayfs.h>
35#include <libdm/dm.h>
Yifan Hong420db9b2019-07-23 20:50:33 -070036#include <libsnapshot/snapshot.h>
Yifan Hong537802d2018-08-15 13:15:42 -070037
Yifan Hong90965502020-02-19 15:22:47 -080038#include "update_engine/cleanup_previous_update_action.h"
Yifan Hong537802d2018-08-15 13:15:42 -070039#include "update_engine/common/boot_control_interface.h"
40#include "update_engine/common/utils.h"
Yifan Hong012508e2019-07-22 18:30:40 -070041#include "update_engine/dynamic_partition_utils.h"
Yifan Hong6a6d0f12020-03-11 13:20:52 -070042#include "update_engine/payload_consumer/delta_performer.h"
Yifan Hong537802d2018-08-15 13:15:42 -070043
44using android::base::GetBoolProperty;
45using android::base::Join;
46using android::dm::DeviceMapper;
47using android::dm::DmDeviceState;
48using android::fs_mgr::CreateLogicalPartition;
David Andersonbb90dfb2019-08-13 14:14:56 -070049using android::fs_mgr::CreateLogicalPartitionParams;
Yifan Hong537802d2018-08-15 13:15:42 -070050using android::fs_mgr::DestroyLogicalPartition;
51using android::fs_mgr::MetadataBuilder;
Yifan Hong012508e2019-07-22 18:30:40 -070052using android::fs_mgr::Partition;
Yifan Hong6e706b12018-11-09 16:50:51 -080053using android::fs_mgr::PartitionOpener;
Yifan Hong012508e2019-07-22 18:30:40 -070054using android::fs_mgr::SlotSuffixForSlotNumber;
Yifan Hongf5261562020-03-10 10:28:10 -070055using android::snapshot::OptimizeSourceCopyOperation;
Yifan Hong0850bca2020-01-16 15:14:07 -080056using android::snapshot::Return;
Yifan Hongf033ecb2020-01-07 18:13:56 -080057using android::snapshot::SnapshotManager;
Yifan Hong2257ee12020-01-13 18:33:00 -080058using android::snapshot::UpdateState;
Yifan Hong537802d2018-08-15 13:15:42 -070059
60namespace chromeos_update_engine {
61
Yifan Hong6e706b12018-11-09 16:50:51 -080062constexpr char kUseDynamicPartitions[] = "ro.boot.dynamic_partitions";
63constexpr char kRetrfoitDynamicPartitions[] =
64 "ro.boot.dynamic_partitions_retrofit";
Yifan Hong413d5722019-07-23 14:21:09 -070065constexpr char kVirtualAbEnabled[] = "ro.virtual_ab.enabled";
66constexpr char kVirtualAbRetrofit[] = "ro.virtual_ab.retrofit";
Yifan Hong420db9b2019-07-23 20:50:33 -070067// Map timeout for dynamic partitions.
68constexpr std::chrono::milliseconds kMapTimeout{1000};
69// Map timeout for dynamic partitions with snapshots. Since several devices
70// needs to be mapped, this timeout is longer than |kMapTimeout|.
71constexpr std::chrono::milliseconds kMapSnapshotTimeout{5000};
72
Yifan Hongbae27842019-10-24 16:56:12 -070073#ifdef __ANDROID_RECOVERY__
74constexpr bool kIsRecovery = true;
75#else
76constexpr bool kIsRecovery = false;
77#endif
78
Yifan Hong537802d2018-08-15 13:15:42 -070079DynamicPartitionControlAndroid::~DynamicPartitionControlAndroid() {
Yifan Hongbae27842019-10-24 16:56:12 -070080 Cleanup();
Yifan Hong537802d2018-08-15 13:15:42 -070081}
82
Yifan Hong186bb682019-07-23 14:04:39 -070083static FeatureFlag GetFeatureFlag(const char* enable_prop,
84 const char* retrofit_prop) {
85 bool retrofit = GetBoolProperty(retrofit_prop, false);
86 bool enabled = GetBoolProperty(enable_prop, false);
87 if (retrofit && !enabled) {
88 LOG(ERROR) << retrofit_prop << " is true but " << enable_prop
89 << " is not. These sysprops are inconsistent. Assume that "
90 << enable_prop << " is true from now on.";
91 }
92 if (retrofit) {
93 return FeatureFlag(FeatureFlag::Value::RETROFIT);
94 }
95 if (enabled) {
96 return FeatureFlag(FeatureFlag::Value::LAUNCH);
97 }
98 return FeatureFlag(FeatureFlag::Value::NONE);
Yifan Hong537802d2018-08-15 13:15:42 -070099}
100
Yifan Hongb38e1af2019-10-17 14:59:22 -0700101DynamicPartitionControlAndroid::DynamicPartitionControlAndroid()
102 : dynamic_partitions_(
103 GetFeatureFlag(kUseDynamicPartitions, kRetrfoitDynamicPartitions)),
104 virtual_ab_(GetFeatureFlag(kVirtualAbEnabled, kVirtualAbRetrofit)) {
105 if (GetVirtualAbFeatureFlag().IsEnabled()) {
Yifan Hongf033ecb2020-01-07 18:13:56 -0800106 snapshot_ = SnapshotManager::New();
Yifan Hongb38e1af2019-10-17 14:59:22 -0700107 CHECK(snapshot_ != nullptr) << "Cannot initialize SnapshotManager.";
108 }
109}
110
Yifan Hong186bb682019-07-23 14:04:39 -0700111FeatureFlag DynamicPartitionControlAndroid::GetDynamicPartitionsFeatureFlag() {
Yifan Hongb38e1af2019-10-17 14:59:22 -0700112 return dynamic_partitions_;
Yifan Hong6e706b12018-11-09 16:50:51 -0800113}
114
Yifan Hong413d5722019-07-23 14:21:09 -0700115FeatureFlag DynamicPartitionControlAndroid::GetVirtualAbFeatureFlag() {
Yifan Hongb38e1af2019-10-17 14:59:22 -0700116 return virtual_ab_;
Yifan Hong413d5722019-07-23 14:21:09 -0700117}
118
Yifan Hongf5261562020-03-10 10:28:10 -0700119bool DynamicPartitionControlAndroid::OptimizeOperation(
120 const std::string& partition_name,
121 const InstallOperation& operation,
122 InstallOperation* optimized) {
Alessio Balsini2a3b4a22019-11-25 16:46:51 +0000123 switch (operation.type()) {
124 case InstallOperation::SOURCE_COPY:
125 return target_supports_snapshot_ &&
126 GetVirtualAbFeatureFlag().IsEnabled() &&
Yifan Hong6eec9952019-12-04 13:12:01 -0800127 mapped_devices_.count(partition_name +
128 SlotSuffixForSlotNumber(target_slot_)) > 0 &&
Yifan Hongf5261562020-03-10 10:28:10 -0700129 OptimizeSourceCopyOperation(operation, optimized);
Alessio Balsini2a3b4a22019-11-25 16:46:51 +0000130 break;
131 default:
132 break;
133 }
Alessio Balsini14980e22019-11-26 11:46:06 +0000134 return false;
135}
136
Yifan Hong8546a712019-03-28 14:42:53 -0700137bool DynamicPartitionControlAndroid::MapPartitionInternal(
Yifan Hong537802d2018-08-15 13:15:42 -0700138 const std::string& super_device,
139 const std::string& target_partition_name,
140 uint32_t slot,
Yifan Hongaf65ef12018-10-29 11:09:06 -0700141 bool force_writable,
Yifan Hong537802d2018-08-15 13:15:42 -0700142 std::string* path) {
David Andersonbb90dfb2019-08-13 14:14:56 -0700143 CreateLogicalPartitionParams params = {
144 .block_device = super_device,
145 .metadata_slot = slot,
146 .partition_name = target_partition_name,
147 .force_writable = force_writable,
David Andersonbb90dfb2019-08-13 14:14:56 -0700148 };
Yifan Hong420db9b2019-07-23 20:50:33 -0700149 bool success = false;
Yifan Hongf0f4a912019-09-26 17:51:33 -0700150 if (GetVirtualAbFeatureFlag().IsEnabled() && target_supports_snapshot_ &&
151 force_writable) {
Yifan Hong420db9b2019-07-23 20:50:33 -0700152 // Only target partitions are mapped with force_writable. On Virtual
153 // A/B devices, target partitions may overlap with source partitions, so
154 // they must be mapped with snapshot.
155 params.timeout_ms = kMapSnapshotTimeout;
156 success = snapshot_->MapUpdateSnapshot(params, path);
157 } else {
158 params.timeout_ms = kMapTimeout;
159 success = CreateLogicalPartition(params, path);
160 }
David Andersonbb90dfb2019-08-13 14:14:56 -0700161
Yifan Hong420db9b2019-07-23 20:50:33 -0700162 if (!success) {
Yifan Hong537802d2018-08-15 13:15:42 -0700163 LOG(ERROR) << "Cannot map " << target_partition_name << " in "
164 << super_device << " on device mapper.";
165 return false;
166 }
167 LOG(INFO) << "Succesfully mapped " << target_partition_name
Yifan Hongaf65ef12018-10-29 11:09:06 -0700168 << " to device mapper (force_writable = " << force_writable
169 << "); device path at " << *path;
Yifan Hong537802d2018-08-15 13:15:42 -0700170 mapped_devices_.insert(target_partition_name);
171 return true;
172}
173
Yifan Hong8546a712019-03-28 14:42:53 -0700174bool DynamicPartitionControlAndroid::MapPartitionOnDeviceMapper(
175 const std::string& super_device,
176 const std::string& target_partition_name,
177 uint32_t slot,
178 bool force_writable,
179 std::string* path) {
180 DmDeviceState state = GetState(target_partition_name);
181 if (state == DmDeviceState::ACTIVE) {
182 if (mapped_devices_.find(target_partition_name) != mapped_devices_.end()) {
183 if (GetDmDevicePathByName(target_partition_name, path)) {
184 LOG(INFO) << target_partition_name
185 << " is mapped on device mapper: " << *path;
186 return true;
187 }
188 LOG(ERROR) << target_partition_name << " is mapped but path is unknown.";
189 return false;
190 }
191 // If target_partition_name is not in mapped_devices_ but state is ACTIVE,
192 // the device might be mapped incorrectly before. Attempt to unmap it.
193 // Note that for source partitions, if GetState() == ACTIVE, callers (e.g.
194 // BootControlAndroid) should not call MapPartitionOnDeviceMapper, but
195 // should directly call GetDmDevicePathByName.
David Anderson4c891c92019-06-21 17:45:23 -0700196 if (!UnmapPartitionOnDeviceMapper(target_partition_name)) {
Yifan Hong8546a712019-03-28 14:42:53 -0700197 LOG(ERROR) << target_partition_name
198 << " is mapped before the update, and it cannot be unmapped.";
199 return false;
200 }
201 state = GetState(target_partition_name);
202 if (state != DmDeviceState::INVALID) {
203 LOG(ERROR) << target_partition_name << " is unmapped but state is "
204 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
205 return false;
206 }
207 }
208 if (state == DmDeviceState::INVALID) {
209 return MapPartitionInternal(
210 super_device, target_partition_name, slot, force_writable, path);
211 }
212
213 LOG(ERROR) << target_partition_name
214 << " is mapped on device mapper but state is unknown: "
215 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
216 return false;
217}
218
Yifan Hong537802d2018-08-15 13:15:42 -0700219bool DynamicPartitionControlAndroid::UnmapPartitionOnDeviceMapper(
David Anderson4c891c92019-06-21 17:45:23 -0700220 const std::string& target_partition_name) {
Yifan Hong537802d2018-08-15 13:15:42 -0700221 if (DeviceMapper::Instance().GetState(target_partition_name) !=
222 DmDeviceState::INVALID) {
Yifan Hong420db9b2019-07-23 20:50:33 -0700223 // Partitions at target slot on non-Virtual A/B devices are mapped as
224 // dm-linear. Also, on Virtual A/B devices, system_other may be mapped for
225 // preopt apps as dm-linear.
226 // Call DestroyLogicalPartition to handle these cases.
227 bool success = DestroyLogicalPartition(target_partition_name);
228
229 // On a Virtual A/B device, |target_partition_name| may be a leftover from
230 // a paused update. Clean up any underlying devices.
231 if (GetVirtualAbFeatureFlag().IsEnabled()) {
232 success &= snapshot_->UnmapUpdateSnapshot(target_partition_name);
233 }
234
235 if (!success) {
Yifan Hong537802d2018-08-15 13:15:42 -0700236 LOG(ERROR) << "Cannot unmap " << target_partition_name
237 << " from device mapper.";
238 return false;
239 }
240 LOG(INFO) << "Successfully unmapped " << target_partition_name
241 << " from device mapper.";
242 }
243 mapped_devices_.erase(target_partition_name);
244 return true;
245}
246
Yifan Hongbae27842019-10-24 16:56:12 -0700247void DynamicPartitionControlAndroid::UnmapAllPartitions() {
Tao Bao8c4d0082019-08-08 08:56:16 -0700248 if (mapped_devices_.empty()) {
249 return;
250 }
Yifan Hong537802d2018-08-15 13:15:42 -0700251 // UnmapPartitionOnDeviceMapper removes objects from mapped_devices_, hence
252 // a copy is needed for the loop.
253 std::set<std::string> mapped = mapped_devices_;
254 LOG(INFO) << "Destroying [" << Join(mapped, ", ") << "] from device mapper";
255 for (const auto& partition_name : mapped) {
David Anderson4c891c92019-06-21 17:45:23 -0700256 ignore_result(UnmapPartitionOnDeviceMapper(partition_name));
Yifan Hong537802d2018-08-15 13:15:42 -0700257 }
258}
259
260void DynamicPartitionControlAndroid::Cleanup() {
Yifan Hongbae27842019-10-24 16:56:12 -0700261 UnmapAllPartitions();
262 metadata_device_.reset();
Yifan Hong537802d2018-08-15 13:15:42 -0700263}
264
265bool DynamicPartitionControlAndroid::DeviceExists(const std::string& path) {
266 return base::PathExists(base::FilePath(path));
267}
268
269android::dm::DmDeviceState DynamicPartitionControlAndroid::GetState(
270 const std::string& name) {
271 return DeviceMapper::Instance().GetState(name);
272}
273
274bool DynamicPartitionControlAndroid::GetDmDevicePathByName(
275 const std::string& name, std::string* path) {
276 return DeviceMapper::Instance().GetDmDevicePathByName(name, path);
277}
278
279std::unique_ptr<MetadataBuilder>
280DynamicPartitionControlAndroid::LoadMetadataBuilder(
Yifan Hong012508e2019-07-22 18:30:40 -0700281 const std::string& super_device, uint32_t source_slot) {
282 return LoadMetadataBuilder(
283 super_device, source_slot, BootControlInterface::kInvalidSlot);
284}
285
286std::unique_ptr<MetadataBuilder>
287DynamicPartitionControlAndroid::LoadMetadataBuilder(
Yifan Hong6e706b12018-11-09 16:50:51 -0800288 const std::string& super_device,
289 uint32_t source_slot,
290 uint32_t target_slot) {
Yifan Hong30fa5f52019-08-05 16:39:59 -0700291 std::unique_ptr<MetadataBuilder> builder;
292 if (target_slot == BootControlInterface::kInvalidSlot) {
293 builder =
294 MetadataBuilder::New(PartitionOpener(), super_device, source_slot);
295 } else {
Yifan Hong50a56c62019-10-14 19:35:05 -0700296 bool always_keep_source_slot = !target_supports_snapshot_;
297 builder = MetadataBuilder::NewForUpdate(PartitionOpener(),
298 super_device,
299 source_slot,
300 target_slot,
301 always_keep_source_slot);
Yifan Hong30fa5f52019-08-05 16:39:59 -0700302 }
Yifan Hong6e706b12018-11-09 16:50:51 -0800303
Yifan Hong537802d2018-08-15 13:15:42 -0700304 if (builder == nullptr) {
305 LOG(WARNING) << "No metadata slot "
306 << BootControlInterface::SlotName(source_slot) << " in "
307 << super_device;
Yifan Hongf48a0052018-10-29 16:30:43 -0700308 return nullptr;
Yifan Hong537802d2018-08-15 13:15:42 -0700309 }
310 LOG(INFO) << "Loaded metadata from slot "
311 << BootControlInterface::SlotName(source_slot) << " in "
312 << super_device;
313 return builder;
314}
315
316bool DynamicPartitionControlAndroid::StoreMetadata(
317 const std::string& super_device,
318 MetadataBuilder* builder,
319 uint32_t target_slot) {
320 auto metadata = builder->Export();
321 if (metadata == nullptr) {
322 LOG(ERROR) << "Cannot export metadata to slot "
323 << BootControlInterface::SlotName(target_slot) << " in "
324 << super_device;
325 return false;
326 }
327
Yifan Hong186bb682019-07-23 14:04:39 -0700328 if (GetDynamicPartitionsFeatureFlag().IsRetrofit()) {
Yifan Hong6e706b12018-11-09 16:50:51 -0800329 if (!FlashPartitionTable(super_device, *metadata)) {
330 LOG(ERROR) << "Cannot write metadata to " << super_device;
331 return false;
332 }
333 LOG(INFO) << "Written metadata to " << super_device;
334 } else {
335 if (!UpdatePartitionTable(super_device, *metadata, target_slot)) {
336 LOG(ERROR) << "Cannot write metadata to slot "
337 << BootControlInterface::SlotName(target_slot) << " in "
338 << super_device;
339 return false;
340 }
341 LOG(INFO) << "Copied metadata to slot "
342 << BootControlInterface::SlotName(target_slot) << " in "
343 << super_device;
Yifan Hong537802d2018-08-15 13:15:42 -0700344 }
345
Yifan Hong537802d2018-08-15 13:15:42 -0700346 return true;
347}
348
349bool DynamicPartitionControlAndroid::GetDeviceDir(std::string* out) {
350 // We can't use fs_mgr to look up |partition_name| because fstab
351 // doesn't list every slot partition (it uses the slotselect option
352 // to mask the suffix).
353 //
354 // We can however assume that there's an entry for the /misc mount
355 // point and use that to get the device file for the misc
356 // partition. This helps us locate the disk that |partition_name|
357 // resides on. From there we'll assume that a by-name scheme is used
358 // so we can just replace the trailing "misc" by the given
359 // |partition_name| and suffix corresponding to |slot|, e.g.
360 //
361 // /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
362 // /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a
363 //
364 // If needed, it's possible to relax the by-name assumption in the
365 // future by trawling /sys/block looking for the appropriate sibling
366 // of misc and then finding an entry in /dev matching the sysfs
367 // entry.
368
369 std::string err, misc_device = get_bootloader_message_blk_device(&err);
370 if (misc_device.empty()) {
371 LOG(ERROR) << "Unable to get misc block device: " << err;
372 return false;
373 }
374
375 if (!utils::IsSymlink(misc_device.c_str())) {
376 LOG(ERROR) << "Device file " << misc_device << " for /misc "
377 << "is not a symlink.";
378 return false;
379 }
380 *out = base::FilePath(misc_device).DirName().value();
381 return true;
382}
Yifan Hong012508e2019-07-22 18:30:40 -0700383
384bool DynamicPartitionControlAndroid::PreparePartitionsForUpdate(
385 uint32_t source_slot,
386 uint32_t target_slot,
Yifan Hongf0f4a912019-09-26 17:51:33 -0700387 const DeltaArchiveManifest& manifest,
Yifan Hongf033ecb2020-01-07 18:13:56 -0800388 bool update,
389 uint64_t* required_size) {
Yifan Hong6eec9952019-12-04 13:12:01 -0800390 source_slot_ = source_slot;
391 target_slot_ = target_slot;
Yifan Hongf033ecb2020-01-07 18:13:56 -0800392 if (required_size != nullptr) {
393 *required_size = 0;
394 }
Yifan Hong6eec9952019-12-04 13:12:01 -0800395
Yifan Hong3a1a5612019-11-05 16:34:32 -0800396 if (fs_mgr_overlayfs_is_setup()) {
397 // Non DAP devices can use overlayfs as well.
398 LOG(WARNING)
399 << "overlayfs overrides are active and can interfere with our "
400 "resources.\n"
401 << "run adb enable-verity to deactivate if required and try again.";
402 }
403
404 if (!GetDynamicPartitionsFeatureFlag().IsEnabled()) {
405 return true;
406 }
407
408 if (target_slot == source_slot) {
409 LOG(ERROR) << "Cannot call PreparePartitionsForUpdate on current slot.";
410 return false;
411 }
412
413 // Although the current build supports dynamic partitions, the given payload
414 // doesn't use it for target partitions. This could happen when applying a
415 // retrofit update. Skip updating the partition metadata for the target slot.
416 is_target_dynamic_ = !manifest.dynamic_partition_metadata().groups().empty();
417 if (!is_target_dynamic_) {
418 return true;
419 }
420
Yifan Hongf0f4a912019-09-26 17:51:33 -0700421 target_supports_snapshot_ =
422 manifest.dynamic_partition_metadata().snapshot_enabled();
423
Yifan Hong2c62c132019-10-24 14:53:40 -0700424 if (GetVirtualAbFeatureFlag().IsEnabled()) {
425 metadata_device_ = snapshot_->EnsureMetadataMounted();
426 TEST_AND_RETURN_FALSE(metadata_device_ != nullptr);
427 }
428
Yifan Hongf0f4a912019-09-26 17:51:33 -0700429 if (!update)
430 return true;
431
Yifan Hongbae27842019-10-24 16:56:12 -0700432 bool delete_source = false;
433
Yifan Hong6d888562019-10-01 13:00:31 -0700434 if (GetVirtualAbFeatureFlag().IsEnabled()) {
435 // On Virtual A/B device, either CancelUpdate() or BeginUpdate() must be
436 // called before calling UnmapUpdateSnapshot.
437 // - If target_supports_snapshot_, PrepareSnapshotPartitionsForUpdate()
438 // calls BeginUpdate() which resets update state
Yifan Hongbae27842019-10-24 16:56:12 -0700439 // - If !target_supports_snapshot_ or PrepareSnapshotPartitionsForUpdate
440 // failed in recovery, explicitly CancelUpdate().
Yifan Hong6d888562019-10-01 13:00:31 -0700441 if (target_supports_snapshot_) {
Yifan Hongbae27842019-10-24 16:56:12 -0700442 if (PrepareSnapshotPartitionsForUpdate(
443 source_slot, target_slot, manifest, required_size)) {
444 return true;
445 }
446
447 // Virtual A/B device doing Virtual A/B update in Android mode must use
448 // snapshots.
449 if (!IsRecovery()) {
450 LOG(ERROR) << "PrepareSnapshotPartitionsForUpdate failed in Android "
451 << "mode";
452 return false;
453 }
454
455 delete_source = true;
456 LOG(INFO) << "PrepareSnapshotPartitionsForUpdate failed in recovery. "
457 << "Attempt to overwrite existing partitions if possible";
458 } else {
459 // Downgrading to an non-Virtual A/B build or is secondary OTA.
460 LOG(INFO) << "Using regular A/B on Virtual A/B because package disabled "
461 << "snapshots.";
Yifan Hong6d888562019-10-01 13:00:31 -0700462 }
Yifan Hongbae27842019-10-24 16:56:12 -0700463
Yifan Hong6d888562019-10-01 13:00:31 -0700464 if (!snapshot_->CancelUpdate()) {
465 LOG(ERROR) << "Cannot cancel previous update.";
466 return false;
467 }
Yifan Hong420db9b2019-07-23 20:50:33 -0700468 }
Yifan Hongbae27842019-10-24 16:56:12 -0700469
470 return PrepareDynamicPartitionsForUpdate(
471 source_slot, target_slot, manifest, delete_source);
Yifan Hong420db9b2019-07-23 20:50:33 -0700472}
473
474bool DynamicPartitionControlAndroid::PrepareDynamicPartitionsForUpdate(
475 uint32_t source_slot,
476 uint32_t target_slot,
Yifan Hongbae27842019-10-24 16:56:12 -0700477 const DeltaArchiveManifest& manifest,
478 bool delete_source) {
Yifan Hong012508e2019-07-22 18:30:40 -0700479 const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
480
481 // Unmap all the target dynamic partitions because they would become
482 // inconsistent with the new metadata.
Yifan Hong13d41cb2019-09-16 13:18:22 -0700483 for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
484 for (const auto& partition_name : group.partition_names()) {
485 if (!UnmapPartitionOnDeviceMapper(partition_name + target_suffix)) {
Yifan Hong012508e2019-07-22 18:30:40 -0700486 return false;
487 }
488 }
489 }
490
491 std::string device_dir_str;
492 if (!GetDeviceDir(&device_dir_str)) {
493 return false;
494 }
495 base::FilePath device_dir(device_dir_str);
496 auto source_device =
Yifan Hong700d7c12019-07-23 20:49:16 -0700497 device_dir.Append(GetSuperPartitionName(source_slot)).value();
Yifan Hong012508e2019-07-22 18:30:40 -0700498
499 auto builder = LoadMetadataBuilder(source_device, source_slot, target_slot);
500 if (builder == nullptr) {
501 LOG(ERROR) << "No metadata at "
502 << BootControlInterface::SlotName(source_slot);
503 return false;
504 }
505
Yifan Hongbae27842019-10-24 16:56:12 -0700506 if (delete_source) {
507 TEST_AND_RETURN_FALSE(
508 DeleteSourcePartitions(builder.get(), source_slot, manifest));
509 }
510
Yifan Hong13d41cb2019-09-16 13:18:22 -0700511 if (!UpdatePartitionMetadata(builder.get(), target_slot, manifest)) {
Yifan Hong012508e2019-07-22 18:30:40 -0700512 return false;
513 }
514
515 auto target_device =
Yifan Hong700d7c12019-07-23 20:49:16 -0700516 device_dir.Append(GetSuperPartitionName(target_slot)).value();
Yifan Hong012508e2019-07-22 18:30:40 -0700517 return StoreMetadata(target_device, builder.get(), target_slot);
518}
519
Yifan Hong420db9b2019-07-23 20:50:33 -0700520bool DynamicPartitionControlAndroid::PrepareSnapshotPartitionsForUpdate(
521 uint32_t source_slot,
522 uint32_t target_slot,
Yifan Hongf033ecb2020-01-07 18:13:56 -0800523 const DeltaArchiveManifest& manifest,
524 uint64_t* required_size) {
Yifan Hong420db9b2019-07-23 20:50:33 -0700525 if (!snapshot_->BeginUpdate()) {
526 LOG(ERROR) << "Cannot begin new update.";
527 return false;
528 }
Yifan Hongf033ecb2020-01-07 18:13:56 -0800529 auto ret = snapshot_->CreateUpdateSnapshots(manifest);
530 if (!ret) {
531 LOG(ERROR) << "Cannot create update snapshots: " << ret.string();
532 if (required_size != nullptr &&
Yifan Hong0850bca2020-01-16 15:14:07 -0800533 ret.error_code() == Return::ErrorCode::NO_SPACE) {
Yifan Hongf033ecb2020-01-07 18:13:56 -0800534 *required_size = ret.required_size();
535 }
Yifan Hong420db9b2019-07-23 20:50:33 -0700536 return false;
537 }
538 return true;
539}
540
Yifan Hong700d7c12019-07-23 20:49:16 -0700541std::string DynamicPartitionControlAndroid::GetSuperPartitionName(
542 uint32_t slot) {
543 return fs_mgr_get_super_partition_name(slot);
544}
545
Yifan Hong012508e2019-07-22 18:30:40 -0700546bool DynamicPartitionControlAndroid::UpdatePartitionMetadata(
547 MetadataBuilder* builder,
548 uint32_t target_slot,
Yifan Hong13d41cb2019-09-16 13:18:22 -0700549 const DeltaArchiveManifest& manifest) {
Yifan Honga4e7da32019-09-30 18:25:03 -0700550 // If applying downgrade from Virtual A/B to non-Virtual A/B, the left-over
551 // COW group needs to be deleted to ensure there are enough space to create
552 // target partitions.
553 builder->RemoveGroupAndPartitions(android::snapshot::kCowGroupName);
554
Yifan Hong012508e2019-07-22 18:30:40 -0700555 const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
556 DeleteGroupsWithSuffix(builder, target_suffix);
557
558 uint64_t total_size = 0;
Yifan Hong13d41cb2019-09-16 13:18:22 -0700559 for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
560 total_size += group.size();
Yifan Hong012508e2019-07-22 18:30:40 -0700561 }
562
563 std::string expr;
564 uint64_t allocatable_space = builder->AllocatableSpace();
Yifan Hong186bb682019-07-23 14:04:39 -0700565 if (!GetDynamicPartitionsFeatureFlag().IsRetrofit()) {
Yifan Hong012508e2019-07-22 18:30:40 -0700566 allocatable_space /= 2;
567 expr = "half of ";
568 }
569 if (total_size > allocatable_space) {
570 LOG(ERROR) << "The maximum size of all groups with suffix " << target_suffix
571 << " (" << total_size << ") has exceeded " << expr
572 << "allocatable space for dynamic partitions "
573 << allocatable_space << ".";
574 return false;
575 }
576
Yifan Hong13d41cb2019-09-16 13:18:22 -0700577 // name of partition(e.g. "system") -> size in bytes
578 std::map<std::string, uint64_t> partition_sizes;
579 for (const auto& partition : manifest.partitions()) {
580 partition_sizes.emplace(partition.partition_name(),
581 partition.new_partition_info().size());
582 }
583
584 for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
585 auto group_name_suffix = group.name() + target_suffix;
586 if (!builder->AddGroup(group_name_suffix, group.size())) {
Yifan Hong012508e2019-07-22 18:30:40 -0700587 LOG(ERROR) << "Cannot add group " << group_name_suffix << " with size "
Yifan Hong13d41cb2019-09-16 13:18:22 -0700588 << group.size();
Yifan Hong012508e2019-07-22 18:30:40 -0700589 return false;
590 }
591 LOG(INFO) << "Added group " << group_name_suffix << " with size "
Yifan Hong13d41cb2019-09-16 13:18:22 -0700592 << group.size();
Yifan Hong012508e2019-07-22 18:30:40 -0700593
Yifan Hong13d41cb2019-09-16 13:18:22 -0700594 for (const auto& partition_name : group.partition_names()) {
595 auto partition_sizes_it = partition_sizes.find(partition_name);
596 if (partition_sizes_it == partition_sizes.end()) {
597 // TODO(tbao): Support auto-filling partition info for framework-only
598 // OTA.
599 LOG(ERROR) << "dynamic_partition_metadata contains partition "
600 << partition_name << " but it is not part of the manifest. "
601 << "This is not supported.";
602 return false;
603 }
604 uint64_t partition_size = partition_sizes_it->second;
605
606 auto partition_name_suffix = partition_name + target_suffix;
Yifan Hong012508e2019-07-22 18:30:40 -0700607 Partition* p = builder->AddPartition(
608 partition_name_suffix, group_name_suffix, LP_PARTITION_ATTR_READONLY);
609 if (!p) {
610 LOG(ERROR) << "Cannot add partition " << partition_name_suffix
611 << " to group " << group_name_suffix;
612 return false;
613 }
Yifan Hong13d41cb2019-09-16 13:18:22 -0700614 if (!builder->ResizePartition(p, partition_size)) {
Yifan Hong012508e2019-07-22 18:30:40 -0700615 LOG(ERROR) << "Cannot resize partition " << partition_name_suffix
Yifan Hong13d41cb2019-09-16 13:18:22 -0700616 << " to size " << partition_size << ". Not enough space?";
Yifan Hong012508e2019-07-22 18:30:40 -0700617 return false;
618 }
619 LOG(INFO) << "Added partition " << partition_name_suffix << " to group "
Yifan Hong13d41cb2019-09-16 13:18:22 -0700620 << group_name_suffix << " with size " << partition_size;
Yifan Hong012508e2019-07-22 18:30:40 -0700621 }
622 }
623
624 return true;
625}
626
Yifan Honga33bca42019-09-03 20:29:45 -0700627bool DynamicPartitionControlAndroid::FinishUpdate() {
Yifan Hongd66ecf12020-02-04 11:15:50 -0800628 if (GetVirtualAbFeatureFlag().IsEnabled() &&
629 snapshot_->GetUpdateState() == UpdateState::Initiated) {
Yifan Hongf0f4a912019-09-26 17:51:33 -0700630 LOG(INFO) << "Snapshot writes are done.";
631 return snapshot_->FinishedSnapshotWrites();
632 }
633 return true;
Yifan Honga33bca42019-09-03 20:29:45 -0700634}
635
Yifan Hong3a1a5612019-11-05 16:34:32 -0800636bool DynamicPartitionControlAndroid::GetPartitionDevice(
637 const std::string& partition_name,
638 uint32_t slot,
639 uint32_t current_slot,
640 std::string* device) {
641 const auto& partition_name_suffix =
642 partition_name + SlotSuffixForSlotNumber(slot);
643 std::string device_dir_str;
644 TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
645 base::FilePath device_dir(device_dir_str);
646
647 // When looking up target partition devices, treat them as static if the
648 // current payload doesn't encode them as dynamic partitions. This may happen
649 // when applying a retrofit update on top of a dynamic-partitions-enabled
650 // build.
651 if (GetDynamicPartitionsFeatureFlag().IsEnabled() &&
652 (slot == current_slot || is_target_dynamic_)) {
653 switch (GetDynamicPartitionDevice(
654 device_dir, partition_name_suffix, slot, current_slot, device)) {
655 case DynamicPartitionDeviceStatus::SUCCESS:
656 return true;
657 case DynamicPartitionDeviceStatus::TRY_STATIC:
658 break;
659 case DynamicPartitionDeviceStatus::ERROR: // fallthrough
660 default:
661 return false;
662 }
663 }
664 base::FilePath path = device_dir.Append(partition_name_suffix);
665 if (!DeviceExists(path.value())) {
666 LOG(ERROR) << "Device file " << path.value() << " does not exist.";
667 return false;
668 }
669
670 *device = path.value();
671 return true;
672}
673
674bool DynamicPartitionControlAndroid::IsSuperBlockDevice(
675 const base::FilePath& device_dir,
676 uint32_t current_slot,
677 const std::string& partition_name_suffix) {
678 std::string source_device =
679 device_dir.Append(GetSuperPartitionName(current_slot)).value();
680 auto source_metadata = LoadMetadataBuilder(source_device, current_slot);
681 return source_metadata->HasBlockDevice(partition_name_suffix);
682}
683
684DynamicPartitionControlAndroid::DynamicPartitionDeviceStatus
685DynamicPartitionControlAndroid::GetDynamicPartitionDevice(
686 const base::FilePath& device_dir,
687 const std::string& partition_name_suffix,
688 uint32_t slot,
689 uint32_t current_slot,
690 std::string* device) {
691 std::string super_device =
692 device_dir.Append(GetSuperPartitionName(slot)).value();
693
694 auto builder = LoadMetadataBuilder(super_device, slot);
695 if (builder == nullptr) {
696 LOG(ERROR) << "No metadata in slot "
697 << BootControlInterface::SlotName(slot);
698 return DynamicPartitionDeviceStatus::ERROR;
699 }
700 if (builder->FindPartition(partition_name_suffix) == nullptr) {
701 LOG(INFO) << partition_name_suffix
702 << " is not in super partition metadata.";
703
704 if (IsSuperBlockDevice(device_dir, current_slot, partition_name_suffix)) {
705 LOG(ERROR) << "The static partition " << partition_name_suffix
706 << " is a block device for current metadata."
707 << "It cannot be used as a logical partition.";
708 return DynamicPartitionDeviceStatus::ERROR;
709 }
710
711 return DynamicPartitionDeviceStatus::TRY_STATIC;
712 }
713
714 if (slot == current_slot) {
715 if (GetState(partition_name_suffix) != DmDeviceState::ACTIVE) {
716 LOG(WARNING) << partition_name_suffix << " is at current slot but it is "
717 << "not mapped. Now try to map it.";
718 } else {
719 if (GetDmDevicePathByName(partition_name_suffix, device)) {
720 LOG(INFO) << partition_name_suffix
721 << " is mapped on device mapper: " << *device;
722 return DynamicPartitionDeviceStatus::SUCCESS;
723 }
724 LOG(ERROR) << partition_name_suffix << "is mapped but path is unknown.";
725 return DynamicPartitionDeviceStatus::ERROR;
726 }
727 }
728
729 bool force_writable = slot != current_slot;
730 if (MapPartitionOnDeviceMapper(
731 super_device, partition_name_suffix, slot, force_writable, device)) {
732 return DynamicPartitionDeviceStatus::SUCCESS;
733 }
734 return DynamicPartitionDeviceStatus::ERROR;
735}
736
Yifan Hong6eec9952019-12-04 13:12:01 -0800737void DynamicPartitionControlAndroid::set_fake_mapped_devices(
738 const std::set<std::string>& fake) {
739 mapped_devices_ = fake;
740}
741
Yifan Hongbae27842019-10-24 16:56:12 -0700742bool DynamicPartitionControlAndroid::IsRecovery() {
743 return kIsRecovery;
744}
745
746static bool IsIncrementalUpdate(const DeltaArchiveManifest& manifest) {
747 const auto& partitions = manifest.partitions();
748 return std::any_of(partitions.begin(), partitions.end(), [](const auto& p) {
749 return p.has_old_partition_info();
750 });
751}
752
753bool DynamicPartitionControlAndroid::DeleteSourcePartitions(
754 MetadataBuilder* builder,
755 uint32_t source_slot,
756 const DeltaArchiveManifest& manifest) {
757 TEST_AND_RETURN_FALSE(IsRecovery());
758
759 if (IsIncrementalUpdate(manifest)) {
760 LOG(ERROR) << "Cannot sideload incremental OTA because snapshots cannot "
761 << "be created.";
762 if (GetVirtualAbFeatureFlag().IsLaunch()) {
763 LOG(ERROR) << "Sideloading incremental updates on devices launches "
764 << " Virtual A/B is not supported.";
765 }
766 return false;
767 }
768
769 LOG(INFO) << "Will overwrite existing partitions. Slot "
770 << BootControlInterface::SlotName(source_slot)
771 << "may be unbootable until update finishes!";
772 const std::string source_suffix = SlotSuffixForSlotNumber(source_slot);
773 DeleteGroupsWithSuffix(builder, source_suffix);
774
775 return true;
776}
777
Yifan Hong90965502020-02-19 15:22:47 -0800778std::unique_ptr<AbstractAction>
779DynamicPartitionControlAndroid::GetCleanupPreviousUpdateAction(
780 BootControlInterface* boot_control,
781 PrefsInterface* prefs,
782 CleanupPreviousUpdateActionDelegateInterface* delegate) {
783 if (!GetVirtualAbFeatureFlag().IsEnabled()) {
784 return std::make_unique<NoOpAction>();
785 }
786 return std::make_unique<CleanupPreviousUpdateAction>(
787 prefs, boot_control, snapshot_.get(), delegate);
788}
789
Yifan Hong6a6d0f12020-03-11 13:20:52 -0700790bool DynamicPartitionControlAndroid::ResetUpdate(PrefsInterface* prefs) {
791 if (!GetVirtualAbFeatureFlag().IsEnabled()) {
792 return true;
793 }
794
795 LOG(INFO) << __func__ << " resetting update state and deleting snapshots.";
796 TEST_AND_RETURN_FALSE(prefs != nullptr);
797
798 // If the device has already booted into the target slot,
799 // ResetUpdateProgress may pass but CancelUpdate fails.
800 // This is expected. A scheduled CleanupPreviousUpdateAction should free
801 // space when it is done.
802 TEST_AND_RETURN_FALSE(DeltaPerformer::ResetUpdateProgress(
803 prefs, false /* quick */, false /* skip dynamic partitions metadata */));
804
805 TEST_AND_RETURN_FALSE(snapshot_->CancelUpdate());
806
807 return true;
808}
809
Yifan Hong537802d2018-08-15 13:15:42 -0700810} // namespace chromeos_update_engine