blob: 1993661e642e110592662a3709e0974bd975f916 [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 Hong537802d2018-08-15 13:15:42 -070042
43using android::base::GetBoolProperty;
44using android::base::Join;
45using android::dm::DeviceMapper;
46using android::dm::DmDeviceState;
47using android::fs_mgr::CreateLogicalPartition;
David Andersonbb90dfb2019-08-13 14:14:56 -070048using android::fs_mgr::CreateLogicalPartitionParams;
Yifan Hong537802d2018-08-15 13:15:42 -070049using android::fs_mgr::DestroyLogicalPartition;
50using android::fs_mgr::MetadataBuilder;
Yifan Hong012508e2019-07-22 18:30:40 -070051using android::fs_mgr::Partition;
Yifan Hong6e706b12018-11-09 16:50:51 -080052using android::fs_mgr::PartitionOpener;
Yifan Hong012508e2019-07-22 18:30:40 -070053using android::fs_mgr::SlotSuffixForSlotNumber;
Yifan Hong0850bca2020-01-16 15:14:07 -080054using android::snapshot::Return;
Yifan Hongf033ecb2020-01-07 18:13:56 -080055using android::snapshot::SnapshotManager;
Alessio Balsini2a3b4a22019-11-25 16:46:51 +000056using android::snapshot::SourceCopyOperationIsClone;
Yifan Hong2257ee12020-01-13 18:33:00 -080057using android::snapshot::UpdateState;
Yifan Hong537802d2018-08-15 13:15:42 -070058
59namespace chromeos_update_engine {
60
Yifan Hong6e706b12018-11-09 16:50:51 -080061constexpr char kUseDynamicPartitions[] = "ro.boot.dynamic_partitions";
62constexpr char kRetrfoitDynamicPartitions[] =
63 "ro.boot.dynamic_partitions_retrofit";
Yifan Hong413d5722019-07-23 14:21:09 -070064constexpr char kVirtualAbEnabled[] = "ro.virtual_ab.enabled";
65constexpr char kVirtualAbRetrofit[] = "ro.virtual_ab.retrofit";
Yifan Hong420db9b2019-07-23 20:50:33 -070066// Map timeout for dynamic partitions.
67constexpr std::chrono::milliseconds kMapTimeout{1000};
68// Map timeout for dynamic partitions with snapshots. Since several devices
69// needs to be mapped, this timeout is longer than |kMapTimeout|.
70constexpr std::chrono::milliseconds kMapSnapshotTimeout{5000};
71
Yifan Hongbae27842019-10-24 16:56:12 -070072#ifdef __ANDROID_RECOVERY__
73constexpr bool kIsRecovery = true;
74#else
75constexpr bool kIsRecovery = false;
76#endif
77
Yifan Hong537802d2018-08-15 13:15:42 -070078DynamicPartitionControlAndroid::~DynamicPartitionControlAndroid() {
Yifan Hongbae27842019-10-24 16:56:12 -070079 Cleanup();
Yifan Hong537802d2018-08-15 13:15:42 -070080}
81
Yifan Hong186bb682019-07-23 14:04:39 -070082static FeatureFlag GetFeatureFlag(const char* enable_prop,
83 const char* retrofit_prop) {
84 bool retrofit = GetBoolProperty(retrofit_prop, false);
85 bool enabled = GetBoolProperty(enable_prop, false);
86 if (retrofit && !enabled) {
87 LOG(ERROR) << retrofit_prop << " is true but " << enable_prop
88 << " is not. These sysprops are inconsistent. Assume that "
89 << enable_prop << " is true from now on.";
90 }
91 if (retrofit) {
92 return FeatureFlag(FeatureFlag::Value::RETROFIT);
93 }
94 if (enabled) {
95 return FeatureFlag(FeatureFlag::Value::LAUNCH);
96 }
97 return FeatureFlag(FeatureFlag::Value::NONE);
Yifan Hong537802d2018-08-15 13:15:42 -070098}
99
Yifan Hongb38e1af2019-10-17 14:59:22 -0700100DynamicPartitionControlAndroid::DynamicPartitionControlAndroid()
101 : dynamic_partitions_(
102 GetFeatureFlag(kUseDynamicPartitions, kRetrfoitDynamicPartitions)),
103 virtual_ab_(GetFeatureFlag(kVirtualAbEnabled, kVirtualAbRetrofit)) {
104 if (GetVirtualAbFeatureFlag().IsEnabled()) {
Yifan Hongf033ecb2020-01-07 18:13:56 -0800105 snapshot_ = SnapshotManager::New();
Yifan Hongb38e1af2019-10-17 14:59:22 -0700106 CHECK(snapshot_ != nullptr) << "Cannot initialize SnapshotManager.";
107 }
108}
109
Yifan Hong186bb682019-07-23 14:04:39 -0700110FeatureFlag DynamicPartitionControlAndroid::GetDynamicPartitionsFeatureFlag() {
Yifan Hongb38e1af2019-10-17 14:59:22 -0700111 return dynamic_partitions_;
Yifan Hong6e706b12018-11-09 16:50:51 -0800112}
113
Yifan Hong413d5722019-07-23 14:21:09 -0700114FeatureFlag DynamicPartitionControlAndroid::GetVirtualAbFeatureFlag() {
Yifan Hongb38e1af2019-10-17 14:59:22 -0700115 return virtual_ab_;
Yifan Hong413d5722019-07-23 14:21:09 -0700116}
117
Alessio Balsini14980e22019-11-26 11:46:06 +0000118bool DynamicPartitionControlAndroid::ShouldSkipOperation(
Yifan Hong6eec9952019-12-04 13:12:01 -0800119 const std::string& partition_name, const InstallOperation& operation) {
Alessio Balsini2a3b4a22019-11-25 16:46:51 +0000120 switch (operation.type()) {
121 case InstallOperation::SOURCE_COPY:
122 return target_supports_snapshot_ &&
123 GetVirtualAbFeatureFlag().IsEnabled() &&
Yifan Hong6eec9952019-12-04 13:12:01 -0800124 mapped_devices_.count(partition_name +
125 SlotSuffixForSlotNumber(target_slot_)) > 0 &&
Alessio Balsini2a3b4a22019-11-25 16:46:51 +0000126 SourceCopyOperationIsClone(operation);
127 break;
128 default:
129 break;
130 }
Alessio Balsini14980e22019-11-26 11:46:06 +0000131 return false;
132}
133
Yifan Hong8546a712019-03-28 14:42:53 -0700134bool DynamicPartitionControlAndroid::MapPartitionInternal(
Yifan Hong537802d2018-08-15 13:15:42 -0700135 const std::string& super_device,
136 const std::string& target_partition_name,
137 uint32_t slot,
Yifan Hongaf65ef12018-10-29 11:09:06 -0700138 bool force_writable,
Yifan Hong537802d2018-08-15 13:15:42 -0700139 std::string* path) {
David Andersonbb90dfb2019-08-13 14:14:56 -0700140 CreateLogicalPartitionParams params = {
141 .block_device = super_device,
142 .metadata_slot = slot,
143 .partition_name = target_partition_name,
144 .force_writable = force_writable,
David Andersonbb90dfb2019-08-13 14:14:56 -0700145 };
Yifan Hong420db9b2019-07-23 20:50:33 -0700146 bool success = false;
Yifan Hongf0f4a912019-09-26 17:51:33 -0700147 if (GetVirtualAbFeatureFlag().IsEnabled() && target_supports_snapshot_ &&
148 force_writable) {
Yifan Hong420db9b2019-07-23 20:50:33 -0700149 // Only target partitions are mapped with force_writable. On Virtual
150 // A/B devices, target partitions may overlap with source partitions, so
151 // they must be mapped with snapshot.
152 params.timeout_ms = kMapSnapshotTimeout;
153 success = snapshot_->MapUpdateSnapshot(params, path);
154 } else {
155 params.timeout_ms = kMapTimeout;
156 success = CreateLogicalPartition(params, path);
157 }
David Andersonbb90dfb2019-08-13 14:14:56 -0700158
Yifan Hong420db9b2019-07-23 20:50:33 -0700159 if (!success) {
Yifan Hong537802d2018-08-15 13:15:42 -0700160 LOG(ERROR) << "Cannot map " << target_partition_name << " in "
161 << super_device << " on device mapper.";
162 return false;
163 }
164 LOG(INFO) << "Succesfully mapped " << target_partition_name
Yifan Hongaf65ef12018-10-29 11:09:06 -0700165 << " to device mapper (force_writable = " << force_writable
166 << "); device path at " << *path;
Yifan Hong537802d2018-08-15 13:15:42 -0700167 mapped_devices_.insert(target_partition_name);
168 return true;
169}
170
Yifan Hong8546a712019-03-28 14:42:53 -0700171bool DynamicPartitionControlAndroid::MapPartitionOnDeviceMapper(
172 const std::string& super_device,
173 const std::string& target_partition_name,
174 uint32_t slot,
175 bool force_writable,
176 std::string* path) {
177 DmDeviceState state = GetState(target_partition_name);
178 if (state == DmDeviceState::ACTIVE) {
179 if (mapped_devices_.find(target_partition_name) != mapped_devices_.end()) {
180 if (GetDmDevicePathByName(target_partition_name, path)) {
181 LOG(INFO) << target_partition_name
182 << " is mapped on device mapper: " << *path;
183 return true;
184 }
185 LOG(ERROR) << target_partition_name << " is mapped but path is unknown.";
186 return false;
187 }
188 // If target_partition_name is not in mapped_devices_ but state is ACTIVE,
189 // the device might be mapped incorrectly before. Attempt to unmap it.
190 // Note that for source partitions, if GetState() == ACTIVE, callers (e.g.
191 // BootControlAndroid) should not call MapPartitionOnDeviceMapper, but
192 // should directly call GetDmDevicePathByName.
David Anderson4c891c92019-06-21 17:45:23 -0700193 if (!UnmapPartitionOnDeviceMapper(target_partition_name)) {
Yifan Hong8546a712019-03-28 14:42:53 -0700194 LOG(ERROR) << target_partition_name
195 << " is mapped before the update, and it cannot be unmapped.";
196 return false;
197 }
198 state = GetState(target_partition_name);
199 if (state != DmDeviceState::INVALID) {
200 LOG(ERROR) << target_partition_name << " is unmapped but state is "
201 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
202 return false;
203 }
204 }
205 if (state == DmDeviceState::INVALID) {
206 return MapPartitionInternal(
207 super_device, target_partition_name, slot, force_writable, path);
208 }
209
210 LOG(ERROR) << target_partition_name
211 << " is mapped on device mapper but state is unknown: "
212 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
213 return false;
214}
215
Yifan Hong537802d2018-08-15 13:15:42 -0700216bool DynamicPartitionControlAndroid::UnmapPartitionOnDeviceMapper(
David Anderson4c891c92019-06-21 17:45:23 -0700217 const std::string& target_partition_name) {
Yifan Hong537802d2018-08-15 13:15:42 -0700218 if (DeviceMapper::Instance().GetState(target_partition_name) !=
219 DmDeviceState::INVALID) {
Yifan Hong420db9b2019-07-23 20:50:33 -0700220 // Partitions at target slot on non-Virtual A/B devices are mapped as
221 // dm-linear. Also, on Virtual A/B devices, system_other may be mapped for
222 // preopt apps as dm-linear.
223 // Call DestroyLogicalPartition to handle these cases.
224 bool success = DestroyLogicalPartition(target_partition_name);
225
226 // On a Virtual A/B device, |target_partition_name| may be a leftover from
227 // a paused update. Clean up any underlying devices.
228 if (GetVirtualAbFeatureFlag().IsEnabled()) {
229 success &= snapshot_->UnmapUpdateSnapshot(target_partition_name);
230 }
231
232 if (!success) {
Yifan Hong537802d2018-08-15 13:15:42 -0700233 LOG(ERROR) << "Cannot unmap " << target_partition_name
234 << " from device mapper.";
235 return false;
236 }
237 LOG(INFO) << "Successfully unmapped " << target_partition_name
238 << " from device mapper.";
239 }
240 mapped_devices_.erase(target_partition_name);
241 return true;
242}
243
Yifan Hongbae27842019-10-24 16:56:12 -0700244void DynamicPartitionControlAndroid::UnmapAllPartitions() {
Tao Bao8c4d0082019-08-08 08:56:16 -0700245 if (mapped_devices_.empty()) {
246 return;
247 }
Yifan Hong537802d2018-08-15 13:15:42 -0700248 // UnmapPartitionOnDeviceMapper removes objects from mapped_devices_, hence
249 // a copy is needed for the loop.
250 std::set<std::string> mapped = mapped_devices_;
251 LOG(INFO) << "Destroying [" << Join(mapped, ", ") << "] from device mapper";
252 for (const auto& partition_name : mapped) {
David Anderson4c891c92019-06-21 17:45:23 -0700253 ignore_result(UnmapPartitionOnDeviceMapper(partition_name));
Yifan Hong537802d2018-08-15 13:15:42 -0700254 }
255}
256
257void DynamicPartitionControlAndroid::Cleanup() {
Yifan Hongbae27842019-10-24 16:56:12 -0700258 UnmapAllPartitions();
259 metadata_device_.reset();
Yifan Hong537802d2018-08-15 13:15:42 -0700260}
261
262bool DynamicPartitionControlAndroid::DeviceExists(const std::string& path) {
263 return base::PathExists(base::FilePath(path));
264}
265
266android::dm::DmDeviceState DynamicPartitionControlAndroid::GetState(
267 const std::string& name) {
268 return DeviceMapper::Instance().GetState(name);
269}
270
271bool DynamicPartitionControlAndroid::GetDmDevicePathByName(
272 const std::string& name, std::string* path) {
273 return DeviceMapper::Instance().GetDmDevicePathByName(name, path);
274}
275
276std::unique_ptr<MetadataBuilder>
277DynamicPartitionControlAndroid::LoadMetadataBuilder(
Yifan Hong012508e2019-07-22 18:30:40 -0700278 const std::string& super_device, uint32_t source_slot) {
279 return LoadMetadataBuilder(
280 super_device, source_slot, BootControlInterface::kInvalidSlot);
281}
282
283std::unique_ptr<MetadataBuilder>
284DynamicPartitionControlAndroid::LoadMetadataBuilder(
Yifan Hong6e706b12018-11-09 16:50:51 -0800285 const std::string& super_device,
286 uint32_t source_slot,
287 uint32_t target_slot) {
Yifan Hong30fa5f52019-08-05 16:39:59 -0700288 std::unique_ptr<MetadataBuilder> builder;
289 if (target_slot == BootControlInterface::kInvalidSlot) {
290 builder =
291 MetadataBuilder::New(PartitionOpener(), super_device, source_slot);
292 } else {
Yifan Hong50a56c62019-10-14 19:35:05 -0700293 bool always_keep_source_slot = !target_supports_snapshot_;
294 builder = MetadataBuilder::NewForUpdate(PartitionOpener(),
295 super_device,
296 source_slot,
297 target_slot,
298 always_keep_source_slot);
Yifan Hong30fa5f52019-08-05 16:39:59 -0700299 }
Yifan Hong6e706b12018-11-09 16:50:51 -0800300
Yifan Hong537802d2018-08-15 13:15:42 -0700301 if (builder == nullptr) {
302 LOG(WARNING) << "No metadata slot "
303 << BootControlInterface::SlotName(source_slot) << " in "
304 << super_device;
Yifan Hongf48a0052018-10-29 16:30:43 -0700305 return nullptr;
Yifan Hong537802d2018-08-15 13:15:42 -0700306 }
307 LOG(INFO) << "Loaded metadata from slot "
308 << BootControlInterface::SlotName(source_slot) << " in "
309 << super_device;
310 return builder;
311}
312
313bool DynamicPartitionControlAndroid::StoreMetadata(
314 const std::string& super_device,
315 MetadataBuilder* builder,
316 uint32_t target_slot) {
317 auto metadata = builder->Export();
318 if (metadata == nullptr) {
319 LOG(ERROR) << "Cannot export metadata to slot "
320 << BootControlInterface::SlotName(target_slot) << " in "
321 << super_device;
322 return false;
323 }
324
Yifan Hong186bb682019-07-23 14:04:39 -0700325 if (GetDynamicPartitionsFeatureFlag().IsRetrofit()) {
Yifan Hong6e706b12018-11-09 16:50:51 -0800326 if (!FlashPartitionTable(super_device, *metadata)) {
327 LOG(ERROR) << "Cannot write metadata to " << super_device;
328 return false;
329 }
330 LOG(INFO) << "Written metadata to " << super_device;
331 } else {
332 if (!UpdatePartitionTable(super_device, *metadata, target_slot)) {
333 LOG(ERROR) << "Cannot write metadata to slot "
334 << BootControlInterface::SlotName(target_slot) << " in "
335 << super_device;
336 return false;
337 }
338 LOG(INFO) << "Copied metadata to slot "
339 << BootControlInterface::SlotName(target_slot) << " in "
340 << super_device;
Yifan Hong537802d2018-08-15 13:15:42 -0700341 }
342
Yifan Hong537802d2018-08-15 13:15:42 -0700343 return true;
344}
345
346bool DynamicPartitionControlAndroid::GetDeviceDir(std::string* out) {
347 // We can't use fs_mgr to look up |partition_name| because fstab
348 // doesn't list every slot partition (it uses the slotselect option
349 // to mask the suffix).
350 //
351 // We can however assume that there's an entry for the /misc mount
352 // point and use that to get the device file for the misc
353 // partition. This helps us locate the disk that |partition_name|
354 // resides on. From there we'll assume that a by-name scheme is used
355 // so we can just replace the trailing "misc" by the given
356 // |partition_name| and suffix corresponding to |slot|, e.g.
357 //
358 // /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
359 // /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a
360 //
361 // If needed, it's possible to relax the by-name assumption in the
362 // future by trawling /sys/block looking for the appropriate sibling
363 // of misc and then finding an entry in /dev matching the sysfs
364 // entry.
365
366 std::string err, misc_device = get_bootloader_message_blk_device(&err);
367 if (misc_device.empty()) {
368 LOG(ERROR) << "Unable to get misc block device: " << err;
369 return false;
370 }
371
372 if (!utils::IsSymlink(misc_device.c_str())) {
373 LOG(ERROR) << "Device file " << misc_device << " for /misc "
374 << "is not a symlink.";
375 return false;
376 }
377 *out = base::FilePath(misc_device).DirName().value();
378 return true;
379}
Yifan Hong012508e2019-07-22 18:30:40 -0700380
381bool DynamicPartitionControlAndroid::PreparePartitionsForUpdate(
382 uint32_t source_slot,
383 uint32_t target_slot,
Yifan Hongf0f4a912019-09-26 17:51:33 -0700384 const DeltaArchiveManifest& manifest,
Yifan Hongf033ecb2020-01-07 18:13:56 -0800385 bool update,
386 uint64_t* required_size) {
Yifan Hong6eec9952019-12-04 13:12:01 -0800387 source_slot_ = source_slot;
388 target_slot_ = target_slot;
Yifan Hongf033ecb2020-01-07 18:13:56 -0800389 if (required_size != nullptr) {
390 *required_size = 0;
391 }
Yifan Hong6eec9952019-12-04 13:12:01 -0800392
Yifan Hong3a1a5612019-11-05 16:34:32 -0800393 if (fs_mgr_overlayfs_is_setup()) {
394 // Non DAP devices can use overlayfs as well.
395 LOG(WARNING)
396 << "overlayfs overrides are active and can interfere with our "
397 "resources.\n"
398 << "run adb enable-verity to deactivate if required and try again.";
399 }
400
401 if (!GetDynamicPartitionsFeatureFlag().IsEnabled()) {
402 return true;
403 }
404
405 if (target_slot == source_slot) {
406 LOG(ERROR) << "Cannot call PreparePartitionsForUpdate on current slot.";
407 return false;
408 }
409
410 // Although the current build supports dynamic partitions, the given payload
411 // doesn't use it for target partitions. This could happen when applying a
412 // retrofit update. Skip updating the partition metadata for the target slot.
413 is_target_dynamic_ = !manifest.dynamic_partition_metadata().groups().empty();
414 if (!is_target_dynamic_) {
415 return true;
416 }
417
Yifan Hongf0f4a912019-09-26 17:51:33 -0700418 target_supports_snapshot_ =
419 manifest.dynamic_partition_metadata().snapshot_enabled();
420
Yifan Hong2c62c132019-10-24 14:53:40 -0700421 if (GetVirtualAbFeatureFlag().IsEnabled()) {
422 metadata_device_ = snapshot_->EnsureMetadataMounted();
423 TEST_AND_RETURN_FALSE(metadata_device_ != nullptr);
424 }
425
Yifan Hongf0f4a912019-09-26 17:51:33 -0700426 if (!update)
427 return true;
428
Yifan Hongbae27842019-10-24 16:56:12 -0700429 bool delete_source = false;
430
Yifan Hong6d888562019-10-01 13:00:31 -0700431 if (GetVirtualAbFeatureFlag().IsEnabled()) {
432 // On Virtual A/B device, either CancelUpdate() or BeginUpdate() must be
433 // called before calling UnmapUpdateSnapshot.
434 // - If target_supports_snapshot_, PrepareSnapshotPartitionsForUpdate()
435 // calls BeginUpdate() which resets update state
Yifan Hongbae27842019-10-24 16:56:12 -0700436 // - If !target_supports_snapshot_ or PrepareSnapshotPartitionsForUpdate
437 // failed in recovery, explicitly CancelUpdate().
Yifan Hong6d888562019-10-01 13:00:31 -0700438 if (target_supports_snapshot_) {
Yifan Hongbae27842019-10-24 16:56:12 -0700439 if (PrepareSnapshotPartitionsForUpdate(
440 source_slot, target_slot, manifest, required_size)) {
441 return true;
442 }
443
444 // Virtual A/B device doing Virtual A/B update in Android mode must use
445 // snapshots.
446 if (!IsRecovery()) {
447 LOG(ERROR) << "PrepareSnapshotPartitionsForUpdate failed in Android "
448 << "mode";
449 return false;
450 }
451
452 delete_source = true;
453 LOG(INFO) << "PrepareSnapshotPartitionsForUpdate failed in recovery. "
454 << "Attempt to overwrite existing partitions if possible";
455 } else {
456 // Downgrading to an non-Virtual A/B build or is secondary OTA.
457 LOG(INFO) << "Using regular A/B on Virtual A/B because package disabled "
458 << "snapshots.";
Yifan Hong6d888562019-10-01 13:00:31 -0700459 }
Yifan Hongbae27842019-10-24 16:56:12 -0700460
Yifan Hong6d888562019-10-01 13:00:31 -0700461 if (!snapshot_->CancelUpdate()) {
462 LOG(ERROR) << "Cannot cancel previous update.";
463 return false;
464 }
Yifan Hong420db9b2019-07-23 20:50:33 -0700465 }
Yifan Hongbae27842019-10-24 16:56:12 -0700466
467 return PrepareDynamicPartitionsForUpdate(
468 source_slot, target_slot, manifest, delete_source);
Yifan Hong420db9b2019-07-23 20:50:33 -0700469}
470
471bool DynamicPartitionControlAndroid::PrepareDynamicPartitionsForUpdate(
472 uint32_t source_slot,
473 uint32_t target_slot,
Yifan Hongbae27842019-10-24 16:56:12 -0700474 const DeltaArchiveManifest& manifest,
475 bool delete_source) {
Yifan Hong012508e2019-07-22 18:30:40 -0700476 const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
477
478 // Unmap all the target dynamic partitions because they would become
479 // inconsistent with the new metadata.
Yifan Hong13d41cb2019-09-16 13:18:22 -0700480 for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
481 for (const auto& partition_name : group.partition_names()) {
482 if (!UnmapPartitionOnDeviceMapper(partition_name + target_suffix)) {
Yifan Hong012508e2019-07-22 18:30:40 -0700483 return false;
484 }
485 }
486 }
487
488 std::string device_dir_str;
489 if (!GetDeviceDir(&device_dir_str)) {
490 return false;
491 }
492 base::FilePath device_dir(device_dir_str);
493 auto source_device =
Yifan Hong700d7c12019-07-23 20:49:16 -0700494 device_dir.Append(GetSuperPartitionName(source_slot)).value();
Yifan Hong012508e2019-07-22 18:30:40 -0700495
496 auto builder = LoadMetadataBuilder(source_device, source_slot, target_slot);
497 if (builder == nullptr) {
498 LOG(ERROR) << "No metadata at "
499 << BootControlInterface::SlotName(source_slot);
500 return false;
501 }
502
Yifan Hongbae27842019-10-24 16:56:12 -0700503 if (delete_source) {
504 TEST_AND_RETURN_FALSE(
505 DeleteSourcePartitions(builder.get(), source_slot, manifest));
506 }
507
Yifan Hong13d41cb2019-09-16 13:18:22 -0700508 if (!UpdatePartitionMetadata(builder.get(), target_slot, manifest)) {
Yifan Hong012508e2019-07-22 18:30:40 -0700509 return false;
510 }
511
512 auto target_device =
Yifan Hong700d7c12019-07-23 20:49:16 -0700513 device_dir.Append(GetSuperPartitionName(target_slot)).value();
Yifan Hong012508e2019-07-22 18:30:40 -0700514 return StoreMetadata(target_device, builder.get(), target_slot);
515}
516
Yifan Hong420db9b2019-07-23 20:50:33 -0700517bool DynamicPartitionControlAndroid::PrepareSnapshotPartitionsForUpdate(
518 uint32_t source_slot,
519 uint32_t target_slot,
Yifan Hongf033ecb2020-01-07 18:13:56 -0800520 const DeltaArchiveManifest& manifest,
521 uint64_t* required_size) {
Yifan Hong420db9b2019-07-23 20:50:33 -0700522 if (!snapshot_->BeginUpdate()) {
523 LOG(ERROR) << "Cannot begin new update.";
524 return false;
525 }
Yifan Hongf033ecb2020-01-07 18:13:56 -0800526 auto ret = snapshot_->CreateUpdateSnapshots(manifest);
527 if (!ret) {
528 LOG(ERROR) << "Cannot create update snapshots: " << ret.string();
529 if (required_size != nullptr &&
Yifan Hong0850bca2020-01-16 15:14:07 -0800530 ret.error_code() == Return::ErrorCode::NO_SPACE) {
Yifan Hongf033ecb2020-01-07 18:13:56 -0800531 *required_size = ret.required_size();
532 }
Yifan Hong420db9b2019-07-23 20:50:33 -0700533 return false;
534 }
535 return true;
536}
537
Yifan Hong700d7c12019-07-23 20:49:16 -0700538std::string DynamicPartitionControlAndroid::GetSuperPartitionName(
539 uint32_t slot) {
540 return fs_mgr_get_super_partition_name(slot);
541}
542
Yifan Hong012508e2019-07-22 18:30:40 -0700543bool DynamicPartitionControlAndroid::UpdatePartitionMetadata(
544 MetadataBuilder* builder,
545 uint32_t target_slot,
Yifan Hong13d41cb2019-09-16 13:18:22 -0700546 const DeltaArchiveManifest& manifest) {
Yifan Honga4e7da32019-09-30 18:25:03 -0700547 // If applying downgrade from Virtual A/B to non-Virtual A/B, the left-over
548 // COW group needs to be deleted to ensure there are enough space to create
549 // target partitions.
550 builder->RemoveGroupAndPartitions(android::snapshot::kCowGroupName);
551
Yifan Hong012508e2019-07-22 18:30:40 -0700552 const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
553 DeleteGroupsWithSuffix(builder, target_suffix);
554
555 uint64_t total_size = 0;
Yifan Hong13d41cb2019-09-16 13:18:22 -0700556 for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
557 total_size += group.size();
Yifan Hong012508e2019-07-22 18:30:40 -0700558 }
559
560 std::string expr;
561 uint64_t allocatable_space = builder->AllocatableSpace();
Yifan Hong186bb682019-07-23 14:04:39 -0700562 if (!GetDynamicPartitionsFeatureFlag().IsRetrofit()) {
Yifan Hong012508e2019-07-22 18:30:40 -0700563 allocatable_space /= 2;
564 expr = "half of ";
565 }
566 if (total_size > allocatable_space) {
567 LOG(ERROR) << "The maximum size of all groups with suffix " << target_suffix
568 << " (" << total_size << ") has exceeded " << expr
569 << "allocatable space for dynamic partitions "
570 << allocatable_space << ".";
571 return false;
572 }
573
Yifan Hong13d41cb2019-09-16 13:18:22 -0700574 // name of partition(e.g. "system") -> size in bytes
575 std::map<std::string, uint64_t> partition_sizes;
576 for (const auto& partition : manifest.partitions()) {
577 partition_sizes.emplace(partition.partition_name(),
578 partition.new_partition_info().size());
579 }
580
581 for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
582 auto group_name_suffix = group.name() + target_suffix;
583 if (!builder->AddGroup(group_name_suffix, group.size())) {
Yifan Hong012508e2019-07-22 18:30:40 -0700584 LOG(ERROR) << "Cannot add group " << group_name_suffix << " with size "
Yifan Hong13d41cb2019-09-16 13:18:22 -0700585 << group.size();
Yifan Hong012508e2019-07-22 18:30:40 -0700586 return false;
587 }
588 LOG(INFO) << "Added group " << group_name_suffix << " with size "
Yifan Hong13d41cb2019-09-16 13:18:22 -0700589 << group.size();
Yifan Hong012508e2019-07-22 18:30:40 -0700590
Yifan Hong13d41cb2019-09-16 13:18:22 -0700591 for (const auto& partition_name : group.partition_names()) {
592 auto partition_sizes_it = partition_sizes.find(partition_name);
593 if (partition_sizes_it == partition_sizes.end()) {
594 // TODO(tbao): Support auto-filling partition info for framework-only
595 // OTA.
596 LOG(ERROR) << "dynamic_partition_metadata contains partition "
597 << partition_name << " but it is not part of the manifest. "
598 << "This is not supported.";
599 return false;
600 }
601 uint64_t partition_size = partition_sizes_it->second;
602
603 auto partition_name_suffix = partition_name + target_suffix;
Yifan Hong012508e2019-07-22 18:30:40 -0700604 Partition* p = builder->AddPartition(
605 partition_name_suffix, group_name_suffix, LP_PARTITION_ATTR_READONLY);
606 if (!p) {
607 LOG(ERROR) << "Cannot add partition " << partition_name_suffix
608 << " to group " << group_name_suffix;
609 return false;
610 }
Yifan Hong13d41cb2019-09-16 13:18:22 -0700611 if (!builder->ResizePartition(p, partition_size)) {
Yifan Hong012508e2019-07-22 18:30:40 -0700612 LOG(ERROR) << "Cannot resize partition " << partition_name_suffix
Yifan Hong13d41cb2019-09-16 13:18:22 -0700613 << " to size " << partition_size << ". Not enough space?";
Yifan Hong012508e2019-07-22 18:30:40 -0700614 return false;
615 }
616 LOG(INFO) << "Added partition " << partition_name_suffix << " to group "
Yifan Hong13d41cb2019-09-16 13:18:22 -0700617 << group_name_suffix << " with size " << partition_size;
Yifan Hong012508e2019-07-22 18:30:40 -0700618 }
619 }
620
621 return true;
622}
623
Yifan Honga33bca42019-09-03 20:29:45 -0700624bool DynamicPartitionControlAndroid::FinishUpdate() {
Yifan Hongd66ecf12020-02-04 11:15:50 -0800625 if (GetVirtualAbFeatureFlag().IsEnabled() &&
626 snapshot_->GetUpdateState() == UpdateState::Initiated) {
Yifan Hongf0f4a912019-09-26 17:51:33 -0700627 LOG(INFO) << "Snapshot writes are done.";
628 return snapshot_->FinishedSnapshotWrites();
629 }
630 return true;
Yifan Honga33bca42019-09-03 20:29:45 -0700631}
632
Yifan Hong3a1a5612019-11-05 16:34:32 -0800633bool DynamicPartitionControlAndroid::GetPartitionDevice(
634 const std::string& partition_name,
635 uint32_t slot,
636 uint32_t current_slot,
637 std::string* device) {
638 const auto& partition_name_suffix =
639 partition_name + SlotSuffixForSlotNumber(slot);
640 std::string device_dir_str;
641 TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
642 base::FilePath device_dir(device_dir_str);
643
644 // When looking up target partition devices, treat them as static if the
645 // current payload doesn't encode them as dynamic partitions. This may happen
646 // when applying a retrofit update on top of a dynamic-partitions-enabled
647 // build.
648 if (GetDynamicPartitionsFeatureFlag().IsEnabled() &&
649 (slot == current_slot || is_target_dynamic_)) {
650 switch (GetDynamicPartitionDevice(
651 device_dir, partition_name_suffix, slot, current_slot, device)) {
652 case DynamicPartitionDeviceStatus::SUCCESS:
653 return true;
654 case DynamicPartitionDeviceStatus::TRY_STATIC:
655 break;
656 case DynamicPartitionDeviceStatus::ERROR: // fallthrough
657 default:
658 return false;
659 }
660 }
661 base::FilePath path = device_dir.Append(partition_name_suffix);
662 if (!DeviceExists(path.value())) {
663 LOG(ERROR) << "Device file " << path.value() << " does not exist.";
664 return false;
665 }
666
667 *device = path.value();
668 return true;
669}
670
671bool DynamicPartitionControlAndroid::IsSuperBlockDevice(
672 const base::FilePath& device_dir,
673 uint32_t current_slot,
674 const std::string& partition_name_suffix) {
675 std::string source_device =
676 device_dir.Append(GetSuperPartitionName(current_slot)).value();
677 auto source_metadata = LoadMetadataBuilder(source_device, current_slot);
678 return source_metadata->HasBlockDevice(partition_name_suffix);
679}
680
681DynamicPartitionControlAndroid::DynamicPartitionDeviceStatus
682DynamicPartitionControlAndroid::GetDynamicPartitionDevice(
683 const base::FilePath& device_dir,
684 const std::string& partition_name_suffix,
685 uint32_t slot,
686 uint32_t current_slot,
687 std::string* device) {
688 std::string super_device =
689 device_dir.Append(GetSuperPartitionName(slot)).value();
690
691 auto builder = LoadMetadataBuilder(super_device, slot);
692 if (builder == nullptr) {
693 LOG(ERROR) << "No metadata in slot "
694 << BootControlInterface::SlotName(slot);
695 return DynamicPartitionDeviceStatus::ERROR;
696 }
697 if (builder->FindPartition(partition_name_suffix) == nullptr) {
698 LOG(INFO) << partition_name_suffix
699 << " is not in super partition metadata.";
700
701 if (IsSuperBlockDevice(device_dir, current_slot, partition_name_suffix)) {
702 LOG(ERROR) << "The static partition " << partition_name_suffix
703 << " is a block device for current metadata."
704 << "It cannot be used as a logical partition.";
705 return DynamicPartitionDeviceStatus::ERROR;
706 }
707
708 return DynamicPartitionDeviceStatus::TRY_STATIC;
709 }
710
711 if (slot == current_slot) {
712 if (GetState(partition_name_suffix) != DmDeviceState::ACTIVE) {
713 LOG(WARNING) << partition_name_suffix << " is at current slot but it is "
714 << "not mapped. Now try to map it.";
715 } else {
716 if (GetDmDevicePathByName(partition_name_suffix, device)) {
717 LOG(INFO) << partition_name_suffix
718 << " is mapped on device mapper: " << *device;
719 return DynamicPartitionDeviceStatus::SUCCESS;
720 }
721 LOG(ERROR) << partition_name_suffix << "is mapped but path is unknown.";
722 return DynamicPartitionDeviceStatus::ERROR;
723 }
724 }
725
726 bool force_writable = slot != current_slot;
727 if (MapPartitionOnDeviceMapper(
728 super_device, partition_name_suffix, slot, force_writable, device)) {
729 return DynamicPartitionDeviceStatus::SUCCESS;
730 }
731 return DynamicPartitionDeviceStatus::ERROR;
732}
733
Yifan Hong6eec9952019-12-04 13:12:01 -0800734void DynamicPartitionControlAndroid::set_fake_mapped_devices(
735 const std::set<std::string>& fake) {
736 mapped_devices_ = fake;
737}
738
Yifan Hongbae27842019-10-24 16:56:12 -0700739bool DynamicPartitionControlAndroid::IsRecovery() {
740 return kIsRecovery;
741}
742
743static bool IsIncrementalUpdate(const DeltaArchiveManifest& manifest) {
744 const auto& partitions = manifest.partitions();
745 return std::any_of(partitions.begin(), partitions.end(), [](const auto& p) {
746 return p.has_old_partition_info();
747 });
748}
749
750bool DynamicPartitionControlAndroid::DeleteSourcePartitions(
751 MetadataBuilder* builder,
752 uint32_t source_slot,
753 const DeltaArchiveManifest& manifest) {
754 TEST_AND_RETURN_FALSE(IsRecovery());
755
756 if (IsIncrementalUpdate(manifest)) {
757 LOG(ERROR) << "Cannot sideload incremental OTA because snapshots cannot "
758 << "be created.";
759 if (GetVirtualAbFeatureFlag().IsLaunch()) {
760 LOG(ERROR) << "Sideloading incremental updates on devices launches "
761 << " Virtual A/B is not supported.";
762 }
763 return false;
764 }
765
766 LOG(INFO) << "Will overwrite existing partitions. Slot "
767 << BootControlInterface::SlotName(source_slot)
768 << "may be unbootable until update finishes!";
769 const std::string source_suffix = SlotSuffixForSlotNumber(source_slot);
770 DeleteGroupsWithSuffix(builder, source_suffix);
771
772 return true;
773}
774
Yifan Hong90965502020-02-19 15:22:47 -0800775std::unique_ptr<AbstractAction>
776DynamicPartitionControlAndroid::GetCleanupPreviousUpdateAction(
777 BootControlInterface* boot_control,
778 PrefsInterface* prefs,
779 CleanupPreviousUpdateActionDelegateInterface* delegate) {
780 if (!GetVirtualAbFeatureFlag().IsEnabled()) {
781 return std::make_unique<NoOpAction>();
782 }
783 return std::make_unique<CleanupPreviousUpdateAction>(
784 prefs, boot_control, snapshot_.get(), delegate);
785}
786
Yifan Hong537802d2018-08-15 13:15:42 -0700787} // namespace chromeos_update_engine