blob: 072a3ecb996f311f8b61f2a6f25cb1ded2c048a2 [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
38#include "update_engine/common/boot_control_interface.h"
39#include "update_engine/common/utils.h"
Yifan Hong012508e2019-07-22 18:30:40 -070040#include "update_engine/dynamic_partition_utils.h"
Yifan Hong537802d2018-08-15 13:15:42 -070041
42using android::base::GetBoolProperty;
43using android::base::Join;
44using android::dm::DeviceMapper;
45using android::dm::DmDeviceState;
46using android::fs_mgr::CreateLogicalPartition;
David Andersonbb90dfb2019-08-13 14:14:56 -070047using android::fs_mgr::CreateLogicalPartitionParams;
Yifan Hong537802d2018-08-15 13:15:42 -070048using android::fs_mgr::DestroyLogicalPartition;
49using android::fs_mgr::MetadataBuilder;
Yifan Hong012508e2019-07-22 18:30:40 -070050using android::fs_mgr::Partition;
Yifan Hong6e706b12018-11-09 16:50:51 -080051using android::fs_mgr::PartitionOpener;
Yifan Hong012508e2019-07-22 18:30:40 -070052using android::fs_mgr::SlotSuffixForSlotNumber;
Yifan Hong0850bca2020-01-16 15:14:07 -080053using android::snapshot::Return;
Yifan Hongf033ecb2020-01-07 18:13:56 -080054using android::snapshot::SnapshotManager;
Alessio Balsini2a3b4a22019-11-25 16:46:51 +000055using android::snapshot::SourceCopyOperationIsClone;
Yifan Hong2257ee12020-01-13 18:33:00 -080056using android::snapshot::UpdateState;
Yifan Hong537802d2018-08-15 13:15:42 -070057
58namespace chromeos_update_engine {
59
Yifan Hong6e706b12018-11-09 16:50:51 -080060constexpr char kUseDynamicPartitions[] = "ro.boot.dynamic_partitions";
61constexpr char kRetrfoitDynamicPartitions[] =
62 "ro.boot.dynamic_partitions_retrofit";
Yifan Hong413d5722019-07-23 14:21:09 -070063constexpr char kVirtualAbEnabled[] = "ro.virtual_ab.enabled";
64constexpr char kVirtualAbRetrofit[] = "ro.virtual_ab.retrofit";
Yifan Hong420db9b2019-07-23 20:50:33 -070065// Map timeout for dynamic partitions.
66constexpr std::chrono::milliseconds kMapTimeout{1000};
67// Map timeout for dynamic partitions with snapshots. Since several devices
68// needs to be mapped, this timeout is longer than |kMapTimeout|.
69constexpr std::chrono::milliseconds kMapSnapshotTimeout{5000};
70
Yifan Hongbae27842019-10-24 16:56:12 -070071#ifdef __ANDROID_RECOVERY__
72constexpr bool kIsRecovery = true;
73#else
74constexpr bool kIsRecovery = false;
75#endif
76
Yifan Hong537802d2018-08-15 13:15:42 -070077DynamicPartitionControlAndroid::~DynamicPartitionControlAndroid() {
Yifan Hongbae27842019-10-24 16:56:12 -070078 Cleanup();
Yifan Hong537802d2018-08-15 13:15:42 -070079}
80
Yifan Hong186bb682019-07-23 14:04:39 -070081static FeatureFlag GetFeatureFlag(const char* enable_prop,
82 const char* retrofit_prop) {
83 bool retrofit = GetBoolProperty(retrofit_prop, false);
84 bool enabled = GetBoolProperty(enable_prop, false);
85 if (retrofit && !enabled) {
86 LOG(ERROR) << retrofit_prop << " is true but " << enable_prop
87 << " is not. These sysprops are inconsistent. Assume that "
88 << enable_prop << " is true from now on.";
89 }
90 if (retrofit) {
91 return FeatureFlag(FeatureFlag::Value::RETROFIT);
92 }
93 if (enabled) {
94 return FeatureFlag(FeatureFlag::Value::LAUNCH);
95 }
96 return FeatureFlag(FeatureFlag::Value::NONE);
Yifan Hong537802d2018-08-15 13:15:42 -070097}
98
Yifan Hongb38e1af2019-10-17 14:59:22 -070099DynamicPartitionControlAndroid::DynamicPartitionControlAndroid()
100 : dynamic_partitions_(
101 GetFeatureFlag(kUseDynamicPartitions, kRetrfoitDynamicPartitions)),
102 virtual_ab_(GetFeatureFlag(kVirtualAbEnabled, kVirtualAbRetrofit)) {
103 if (GetVirtualAbFeatureFlag().IsEnabled()) {
Yifan Hongf033ecb2020-01-07 18:13:56 -0800104 snapshot_ = SnapshotManager::New();
Yifan Hongb38e1af2019-10-17 14:59:22 -0700105 CHECK(snapshot_ != nullptr) << "Cannot initialize SnapshotManager.";
106 }
107}
108
Yifan Hong186bb682019-07-23 14:04:39 -0700109FeatureFlag DynamicPartitionControlAndroid::GetDynamicPartitionsFeatureFlag() {
Yifan Hongb38e1af2019-10-17 14:59:22 -0700110 return dynamic_partitions_;
Yifan Hong6e706b12018-11-09 16:50:51 -0800111}
112
Yifan Hong413d5722019-07-23 14:21:09 -0700113FeatureFlag DynamicPartitionControlAndroid::GetVirtualAbFeatureFlag() {
Yifan Hongb38e1af2019-10-17 14:59:22 -0700114 return virtual_ab_;
Yifan Hong413d5722019-07-23 14:21:09 -0700115}
116
Alessio Balsini14980e22019-11-26 11:46:06 +0000117bool DynamicPartitionControlAndroid::ShouldSkipOperation(
Yifan Hong6eec9952019-12-04 13:12:01 -0800118 const std::string& partition_name, const InstallOperation& operation) {
Alessio Balsini2a3b4a22019-11-25 16:46:51 +0000119 switch (operation.type()) {
120 case InstallOperation::SOURCE_COPY:
121 return target_supports_snapshot_ &&
122 GetVirtualAbFeatureFlag().IsEnabled() &&
Yifan Hong6eec9952019-12-04 13:12:01 -0800123 mapped_devices_.count(partition_name +
124 SlotSuffixForSlotNumber(target_slot_)) > 0 &&
Alessio Balsini2a3b4a22019-11-25 16:46:51 +0000125 SourceCopyOperationIsClone(operation);
126 break;
127 default:
128 break;
129 }
Alessio Balsini14980e22019-11-26 11:46:06 +0000130 return false;
131}
132
Yifan Hong8546a712019-03-28 14:42:53 -0700133bool DynamicPartitionControlAndroid::MapPartitionInternal(
Yifan Hong537802d2018-08-15 13:15:42 -0700134 const std::string& super_device,
135 const std::string& target_partition_name,
136 uint32_t slot,
Yifan Hongaf65ef12018-10-29 11:09:06 -0700137 bool force_writable,
Yifan Hong537802d2018-08-15 13:15:42 -0700138 std::string* path) {
David Andersonbb90dfb2019-08-13 14:14:56 -0700139 CreateLogicalPartitionParams params = {
140 .block_device = super_device,
141 .metadata_slot = slot,
142 .partition_name = target_partition_name,
143 .force_writable = force_writable,
David Andersonbb90dfb2019-08-13 14:14:56 -0700144 };
Yifan Hong420db9b2019-07-23 20:50:33 -0700145 bool success = false;
Yifan Hongf0f4a912019-09-26 17:51:33 -0700146 if (GetVirtualAbFeatureFlag().IsEnabled() && target_supports_snapshot_ &&
147 force_writable) {
Yifan Hong420db9b2019-07-23 20:50:33 -0700148 // Only target partitions are mapped with force_writable. On Virtual
149 // A/B devices, target partitions may overlap with source partitions, so
150 // they must be mapped with snapshot.
151 params.timeout_ms = kMapSnapshotTimeout;
152 success = snapshot_->MapUpdateSnapshot(params, path);
153 } else {
154 params.timeout_ms = kMapTimeout;
155 success = CreateLogicalPartition(params, path);
156 }
David Andersonbb90dfb2019-08-13 14:14:56 -0700157
Yifan Hong420db9b2019-07-23 20:50:33 -0700158 if (!success) {
Yifan Hong537802d2018-08-15 13:15:42 -0700159 LOG(ERROR) << "Cannot map " << target_partition_name << " in "
160 << super_device << " on device mapper.";
161 return false;
162 }
163 LOG(INFO) << "Succesfully mapped " << target_partition_name
Yifan Hongaf65ef12018-10-29 11:09:06 -0700164 << " to device mapper (force_writable = " << force_writable
165 << "); device path at " << *path;
Yifan Hong537802d2018-08-15 13:15:42 -0700166 mapped_devices_.insert(target_partition_name);
167 return true;
168}
169
Yifan Hong8546a712019-03-28 14:42:53 -0700170bool DynamicPartitionControlAndroid::MapPartitionOnDeviceMapper(
171 const std::string& super_device,
172 const std::string& target_partition_name,
173 uint32_t slot,
174 bool force_writable,
175 std::string* path) {
176 DmDeviceState state = GetState(target_partition_name);
177 if (state == DmDeviceState::ACTIVE) {
178 if (mapped_devices_.find(target_partition_name) != mapped_devices_.end()) {
179 if (GetDmDevicePathByName(target_partition_name, path)) {
180 LOG(INFO) << target_partition_name
181 << " is mapped on device mapper: " << *path;
182 return true;
183 }
184 LOG(ERROR) << target_partition_name << " is mapped but path is unknown.";
185 return false;
186 }
187 // If target_partition_name is not in mapped_devices_ but state is ACTIVE,
188 // the device might be mapped incorrectly before. Attempt to unmap it.
189 // Note that for source partitions, if GetState() == ACTIVE, callers (e.g.
190 // BootControlAndroid) should not call MapPartitionOnDeviceMapper, but
191 // should directly call GetDmDevicePathByName.
David Anderson4c891c92019-06-21 17:45:23 -0700192 if (!UnmapPartitionOnDeviceMapper(target_partition_name)) {
Yifan Hong8546a712019-03-28 14:42:53 -0700193 LOG(ERROR) << target_partition_name
194 << " is mapped before the update, and it cannot be unmapped.";
195 return false;
196 }
197 state = GetState(target_partition_name);
198 if (state != DmDeviceState::INVALID) {
199 LOG(ERROR) << target_partition_name << " is unmapped but state is "
200 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
201 return false;
202 }
203 }
204 if (state == DmDeviceState::INVALID) {
205 return MapPartitionInternal(
206 super_device, target_partition_name, slot, force_writable, path);
207 }
208
209 LOG(ERROR) << target_partition_name
210 << " is mapped on device mapper but state is unknown: "
211 << static_cast<std::underlying_type_t<DmDeviceState>>(state);
212 return false;
213}
214
Yifan Hong537802d2018-08-15 13:15:42 -0700215bool DynamicPartitionControlAndroid::UnmapPartitionOnDeviceMapper(
David Anderson4c891c92019-06-21 17:45:23 -0700216 const std::string& target_partition_name) {
Yifan Hong537802d2018-08-15 13:15:42 -0700217 if (DeviceMapper::Instance().GetState(target_partition_name) !=
218 DmDeviceState::INVALID) {
Yifan Hong420db9b2019-07-23 20:50:33 -0700219 // Partitions at target slot on non-Virtual A/B devices are mapped as
220 // dm-linear. Also, on Virtual A/B devices, system_other may be mapped for
221 // preopt apps as dm-linear.
222 // Call DestroyLogicalPartition to handle these cases.
223 bool success = DestroyLogicalPartition(target_partition_name);
224
225 // On a Virtual A/B device, |target_partition_name| may be a leftover from
226 // a paused update. Clean up any underlying devices.
227 if (GetVirtualAbFeatureFlag().IsEnabled()) {
228 success &= snapshot_->UnmapUpdateSnapshot(target_partition_name);
229 }
230
231 if (!success) {
Yifan Hong537802d2018-08-15 13:15:42 -0700232 LOG(ERROR) << "Cannot unmap " << target_partition_name
233 << " from device mapper.";
234 return false;
235 }
236 LOG(INFO) << "Successfully unmapped " << target_partition_name
237 << " from device mapper.";
238 }
239 mapped_devices_.erase(target_partition_name);
240 return true;
241}
242
Yifan Hongbae27842019-10-24 16:56:12 -0700243void DynamicPartitionControlAndroid::UnmapAllPartitions() {
Tao Bao8c4d0082019-08-08 08:56:16 -0700244 if (mapped_devices_.empty()) {
245 return;
246 }
Yifan Hong537802d2018-08-15 13:15:42 -0700247 // UnmapPartitionOnDeviceMapper removes objects from mapped_devices_, hence
248 // a copy is needed for the loop.
249 std::set<std::string> mapped = mapped_devices_;
250 LOG(INFO) << "Destroying [" << Join(mapped, ", ") << "] from device mapper";
251 for (const auto& partition_name : mapped) {
David Anderson4c891c92019-06-21 17:45:23 -0700252 ignore_result(UnmapPartitionOnDeviceMapper(partition_name));
Yifan Hong537802d2018-08-15 13:15:42 -0700253 }
254}
255
256void DynamicPartitionControlAndroid::Cleanup() {
Yifan Hongbae27842019-10-24 16:56:12 -0700257 UnmapAllPartitions();
258 metadata_device_.reset();
Yifan Hong537802d2018-08-15 13:15:42 -0700259}
260
261bool DynamicPartitionControlAndroid::DeviceExists(const std::string& path) {
262 return base::PathExists(base::FilePath(path));
263}
264
265android::dm::DmDeviceState DynamicPartitionControlAndroid::GetState(
266 const std::string& name) {
267 return DeviceMapper::Instance().GetState(name);
268}
269
270bool DynamicPartitionControlAndroid::GetDmDevicePathByName(
271 const std::string& name, std::string* path) {
272 return DeviceMapper::Instance().GetDmDevicePathByName(name, path);
273}
274
275std::unique_ptr<MetadataBuilder>
276DynamicPartitionControlAndroid::LoadMetadataBuilder(
Yifan Hong012508e2019-07-22 18:30:40 -0700277 const std::string& super_device, uint32_t source_slot) {
278 return LoadMetadataBuilder(
279 super_device, source_slot, BootControlInterface::kInvalidSlot);
280}
281
282std::unique_ptr<MetadataBuilder>
283DynamicPartitionControlAndroid::LoadMetadataBuilder(
Yifan Hong6e706b12018-11-09 16:50:51 -0800284 const std::string& super_device,
285 uint32_t source_slot,
286 uint32_t target_slot) {
Yifan Hong30fa5f52019-08-05 16:39:59 -0700287 std::unique_ptr<MetadataBuilder> builder;
288 if (target_slot == BootControlInterface::kInvalidSlot) {
289 builder =
290 MetadataBuilder::New(PartitionOpener(), super_device, source_slot);
291 } else {
Yifan Hong50a56c62019-10-14 19:35:05 -0700292 bool always_keep_source_slot = !target_supports_snapshot_;
293 builder = MetadataBuilder::NewForUpdate(PartitionOpener(),
294 super_device,
295 source_slot,
296 target_slot,
297 always_keep_source_slot);
Yifan Hong30fa5f52019-08-05 16:39:59 -0700298 }
Yifan Hong6e706b12018-11-09 16:50:51 -0800299
Yifan Hong537802d2018-08-15 13:15:42 -0700300 if (builder == nullptr) {
301 LOG(WARNING) << "No metadata slot "
302 << BootControlInterface::SlotName(source_slot) << " in "
303 << super_device;
Yifan Hongf48a0052018-10-29 16:30:43 -0700304 return nullptr;
Yifan Hong537802d2018-08-15 13:15:42 -0700305 }
306 LOG(INFO) << "Loaded metadata from slot "
307 << BootControlInterface::SlotName(source_slot) << " in "
308 << super_device;
309 return builder;
310}
311
312bool DynamicPartitionControlAndroid::StoreMetadata(
313 const std::string& super_device,
314 MetadataBuilder* builder,
315 uint32_t target_slot) {
316 auto metadata = builder->Export();
317 if (metadata == nullptr) {
318 LOG(ERROR) << "Cannot export metadata to slot "
319 << BootControlInterface::SlotName(target_slot) << " in "
320 << super_device;
321 return false;
322 }
323
Yifan Hong186bb682019-07-23 14:04:39 -0700324 if (GetDynamicPartitionsFeatureFlag().IsRetrofit()) {
Yifan Hong6e706b12018-11-09 16:50:51 -0800325 if (!FlashPartitionTable(super_device, *metadata)) {
326 LOG(ERROR) << "Cannot write metadata to " << super_device;
327 return false;
328 }
329 LOG(INFO) << "Written metadata to " << super_device;
330 } else {
331 if (!UpdatePartitionTable(super_device, *metadata, target_slot)) {
332 LOG(ERROR) << "Cannot write metadata to slot "
333 << BootControlInterface::SlotName(target_slot) << " in "
334 << super_device;
335 return false;
336 }
337 LOG(INFO) << "Copied metadata to slot "
338 << BootControlInterface::SlotName(target_slot) << " in "
339 << super_device;
Yifan Hong537802d2018-08-15 13:15:42 -0700340 }
341
Yifan Hong537802d2018-08-15 13:15:42 -0700342 return true;
343}
344
345bool DynamicPartitionControlAndroid::GetDeviceDir(std::string* out) {
346 // We can't use fs_mgr to look up |partition_name| because fstab
347 // doesn't list every slot partition (it uses the slotselect option
348 // to mask the suffix).
349 //
350 // We can however assume that there's an entry for the /misc mount
351 // point and use that to get the device file for the misc
352 // partition. This helps us locate the disk that |partition_name|
353 // resides on. From there we'll assume that a by-name scheme is used
354 // so we can just replace the trailing "misc" by the given
355 // |partition_name| and suffix corresponding to |slot|, e.g.
356 //
357 // /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
358 // /dev/block/platform/soc.0/7824900.sdhci/by-name/boot_a
359 //
360 // If needed, it's possible to relax the by-name assumption in the
361 // future by trawling /sys/block looking for the appropriate sibling
362 // of misc and then finding an entry in /dev matching the sysfs
363 // entry.
364
365 std::string err, misc_device = get_bootloader_message_blk_device(&err);
366 if (misc_device.empty()) {
367 LOG(ERROR) << "Unable to get misc block device: " << err;
368 return false;
369 }
370
371 if (!utils::IsSymlink(misc_device.c_str())) {
372 LOG(ERROR) << "Device file " << misc_device << " for /misc "
373 << "is not a symlink.";
374 return false;
375 }
376 *out = base::FilePath(misc_device).DirName().value();
377 return true;
378}
Yifan Hong012508e2019-07-22 18:30:40 -0700379
380bool DynamicPartitionControlAndroid::PreparePartitionsForUpdate(
381 uint32_t source_slot,
382 uint32_t target_slot,
Yifan Hongf0f4a912019-09-26 17:51:33 -0700383 const DeltaArchiveManifest& manifest,
Yifan Hongf033ecb2020-01-07 18:13:56 -0800384 bool update,
385 uint64_t* required_size) {
Yifan Hong6eec9952019-12-04 13:12:01 -0800386 source_slot_ = source_slot;
387 target_slot_ = target_slot;
Yifan Hongf033ecb2020-01-07 18:13:56 -0800388 if (required_size != nullptr) {
389 *required_size = 0;
390 }
Yifan Hong6eec9952019-12-04 13:12:01 -0800391
Yifan Hong3a1a5612019-11-05 16:34:32 -0800392 if (fs_mgr_overlayfs_is_setup()) {
393 // Non DAP devices can use overlayfs as well.
394 LOG(WARNING)
395 << "overlayfs overrides are active and can interfere with our "
396 "resources.\n"
397 << "run adb enable-verity to deactivate if required and try again.";
398 }
399
400 if (!GetDynamicPartitionsFeatureFlag().IsEnabled()) {
401 return true;
402 }
403
404 if (target_slot == source_slot) {
405 LOG(ERROR) << "Cannot call PreparePartitionsForUpdate on current slot.";
406 return false;
407 }
408
409 // Although the current build supports dynamic partitions, the given payload
410 // doesn't use it for target partitions. This could happen when applying a
411 // retrofit update. Skip updating the partition metadata for the target slot.
412 is_target_dynamic_ = !manifest.dynamic_partition_metadata().groups().empty();
413 if (!is_target_dynamic_) {
414 return true;
415 }
416
Yifan Hongf0f4a912019-09-26 17:51:33 -0700417 target_supports_snapshot_ =
418 manifest.dynamic_partition_metadata().snapshot_enabled();
419
Yifan Hong2c62c132019-10-24 14:53:40 -0700420 if (GetVirtualAbFeatureFlag().IsEnabled()) {
421 metadata_device_ = snapshot_->EnsureMetadataMounted();
422 TEST_AND_RETURN_FALSE(metadata_device_ != nullptr);
423 }
424
Yifan Hongf0f4a912019-09-26 17:51:33 -0700425 if (!update)
426 return true;
427
Yifan Hongbae27842019-10-24 16:56:12 -0700428 bool delete_source = false;
429
Yifan Hong6d888562019-10-01 13:00:31 -0700430 if (GetVirtualAbFeatureFlag().IsEnabled()) {
431 // On Virtual A/B device, either CancelUpdate() or BeginUpdate() must be
432 // called before calling UnmapUpdateSnapshot.
433 // - If target_supports_snapshot_, PrepareSnapshotPartitionsForUpdate()
434 // calls BeginUpdate() which resets update state
Yifan Hongbae27842019-10-24 16:56:12 -0700435 // - If !target_supports_snapshot_ or PrepareSnapshotPartitionsForUpdate
436 // failed in recovery, explicitly CancelUpdate().
Yifan Hong6d888562019-10-01 13:00:31 -0700437 if (target_supports_snapshot_) {
Yifan Hongbae27842019-10-24 16:56:12 -0700438 if (PrepareSnapshotPartitionsForUpdate(
439 source_slot, target_slot, manifest, required_size)) {
440 return true;
441 }
442
443 // Virtual A/B device doing Virtual A/B update in Android mode must use
444 // snapshots.
445 if (!IsRecovery()) {
446 LOG(ERROR) << "PrepareSnapshotPartitionsForUpdate failed in Android "
447 << "mode";
448 return false;
449 }
450
451 delete_source = true;
452 LOG(INFO) << "PrepareSnapshotPartitionsForUpdate failed in recovery. "
453 << "Attempt to overwrite existing partitions if possible";
454 } else {
455 // Downgrading to an non-Virtual A/B build or is secondary OTA.
456 LOG(INFO) << "Using regular A/B on Virtual A/B because package disabled "
457 << "snapshots.";
Yifan Hong6d888562019-10-01 13:00:31 -0700458 }
Yifan Hongbae27842019-10-24 16:56:12 -0700459
Yifan Hong6d888562019-10-01 13:00:31 -0700460 if (!snapshot_->CancelUpdate()) {
461 LOG(ERROR) << "Cannot cancel previous update.";
462 return false;
463 }
Yifan Hong420db9b2019-07-23 20:50:33 -0700464 }
Yifan Hongbae27842019-10-24 16:56:12 -0700465
466 return PrepareDynamicPartitionsForUpdate(
467 source_slot, target_slot, manifest, delete_source);
Yifan Hong420db9b2019-07-23 20:50:33 -0700468}
469
470bool DynamicPartitionControlAndroid::PrepareDynamicPartitionsForUpdate(
471 uint32_t source_slot,
472 uint32_t target_slot,
Yifan Hongbae27842019-10-24 16:56:12 -0700473 const DeltaArchiveManifest& manifest,
474 bool delete_source) {
Yifan Hong012508e2019-07-22 18:30:40 -0700475 const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
476
477 // Unmap all the target dynamic partitions because they would become
478 // inconsistent with the new metadata.
Yifan Hong13d41cb2019-09-16 13:18:22 -0700479 for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
480 for (const auto& partition_name : group.partition_names()) {
481 if (!UnmapPartitionOnDeviceMapper(partition_name + target_suffix)) {
Yifan Hong012508e2019-07-22 18:30:40 -0700482 return false;
483 }
484 }
485 }
486
487 std::string device_dir_str;
488 if (!GetDeviceDir(&device_dir_str)) {
489 return false;
490 }
491 base::FilePath device_dir(device_dir_str);
492 auto source_device =
Yifan Hong700d7c12019-07-23 20:49:16 -0700493 device_dir.Append(GetSuperPartitionName(source_slot)).value();
Yifan Hong012508e2019-07-22 18:30:40 -0700494
495 auto builder = LoadMetadataBuilder(source_device, source_slot, target_slot);
496 if (builder == nullptr) {
497 LOG(ERROR) << "No metadata at "
498 << BootControlInterface::SlotName(source_slot);
499 return false;
500 }
501
Yifan Hongbae27842019-10-24 16:56:12 -0700502 if (delete_source) {
503 TEST_AND_RETURN_FALSE(
504 DeleteSourcePartitions(builder.get(), source_slot, manifest));
505 }
506
Yifan Hong13d41cb2019-09-16 13:18:22 -0700507 if (!UpdatePartitionMetadata(builder.get(), target_slot, manifest)) {
Yifan Hong012508e2019-07-22 18:30:40 -0700508 return false;
509 }
510
511 auto target_device =
Yifan Hong700d7c12019-07-23 20:49:16 -0700512 device_dir.Append(GetSuperPartitionName(target_slot)).value();
Yifan Hong012508e2019-07-22 18:30:40 -0700513 return StoreMetadata(target_device, builder.get(), target_slot);
514}
515
Yifan Hong420db9b2019-07-23 20:50:33 -0700516bool DynamicPartitionControlAndroid::PrepareSnapshotPartitionsForUpdate(
517 uint32_t source_slot,
518 uint32_t target_slot,
Yifan Hongf033ecb2020-01-07 18:13:56 -0800519 const DeltaArchiveManifest& manifest,
520 uint64_t* required_size) {
Yifan Hong420db9b2019-07-23 20:50:33 -0700521 if (!snapshot_->BeginUpdate()) {
522 LOG(ERROR) << "Cannot begin new update.";
523 return false;
524 }
Yifan Hongf033ecb2020-01-07 18:13:56 -0800525 auto ret = snapshot_->CreateUpdateSnapshots(manifest);
526 if (!ret) {
527 LOG(ERROR) << "Cannot create update snapshots: " << ret.string();
528 if (required_size != nullptr &&
Yifan Hong0850bca2020-01-16 15:14:07 -0800529 ret.error_code() == Return::ErrorCode::NO_SPACE) {
Yifan Hongf033ecb2020-01-07 18:13:56 -0800530 *required_size = ret.required_size();
531 }
Yifan Hong420db9b2019-07-23 20:50:33 -0700532 return false;
533 }
534 return true;
535}
536
Yifan Hong700d7c12019-07-23 20:49:16 -0700537std::string DynamicPartitionControlAndroid::GetSuperPartitionName(
538 uint32_t slot) {
539 return fs_mgr_get_super_partition_name(slot);
540}
541
Yifan Hong012508e2019-07-22 18:30:40 -0700542bool DynamicPartitionControlAndroid::UpdatePartitionMetadata(
543 MetadataBuilder* builder,
544 uint32_t target_slot,
Yifan Hong13d41cb2019-09-16 13:18:22 -0700545 const DeltaArchiveManifest& manifest) {
Yifan Honga4e7da32019-09-30 18:25:03 -0700546 // If applying downgrade from Virtual A/B to non-Virtual A/B, the left-over
547 // COW group needs to be deleted to ensure there are enough space to create
548 // target partitions.
549 builder->RemoveGroupAndPartitions(android::snapshot::kCowGroupName);
550
Yifan Hong012508e2019-07-22 18:30:40 -0700551 const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
552 DeleteGroupsWithSuffix(builder, target_suffix);
553
554 uint64_t total_size = 0;
Yifan Hong13d41cb2019-09-16 13:18:22 -0700555 for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
556 total_size += group.size();
Yifan Hong012508e2019-07-22 18:30:40 -0700557 }
558
559 std::string expr;
560 uint64_t allocatable_space = builder->AllocatableSpace();
Yifan Hong186bb682019-07-23 14:04:39 -0700561 if (!GetDynamicPartitionsFeatureFlag().IsRetrofit()) {
Yifan Hong012508e2019-07-22 18:30:40 -0700562 allocatable_space /= 2;
563 expr = "half of ";
564 }
565 if (total_size > allocatable_space) {
566 LOG(ERROR) << "The maximum size of all groups with suffix " << target_suffix
567 << " (" << total_size << ") has exceeded " << expr
568 << "allocatable space for dynamic partitions "
569 << allocatable_space << ".";
570 return false;
571 }
572
Yifan Hong13d41cb2019-09-16 13:18:22 -0700573 // name of partition(e.g. "system") -> size in bytes
574 std::map<std::string, uint64_t> partition_sizes;
575 for (const auto& partition : manifest.partitions()) {
576 partition_sizes.emplace(partition.partition_name(),
577 partition.new_partition_info().size());
578 }
579
580 for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
581 auto group_name_suffix = group.name() + target_suffix;
582 if (!builder->AddGroup(group_name_suffix, group.size())) {
Yifan Hong012508e2019-07-22 18:30:40 -0700583 LOG(ERROR) << "Cannot add group " << group_name_suffix << " with size "
Yifan Hong13d41cb2019-09-16 13:18:22 -0700584 << group.size();
Yifan Hong012508e2019-07-22 18:30:40 -0700585 return false;
586 }
587 LOG(INFO) << "Added group " << group_name_suffix << " with size "
Yifan Hong13d41cb2019-09-16 13:18:22 -0700588 << group.size();
Yifan Hong012508e2019-07-22 18:30:40 -0700589
Yifan Hong13d41cb2019-09-16 13:18:22 -0700590 for (const auto& partition_name : group.partition_names()) {
591 auto partition_sizes_it = partition_sizes.find(partition_name);
592 if (partition_sizes_it == partition_sizes.end()) {
593 // TODO(tbao): Support auto-filling partition info for framework-only
594 // OTA.
595 LOG(ERROR) << "dynamic_partition_metadata contains partition "
596 << partition_name << " but it is not part of the manifest. "
597 << "This is not supported.";
598 return false;
599 }
600 uint64_t partition_size = partition_sizes_it->second;
601
602 auto partition_name_suffix = partition_name + target_suffix;
Yifan Hong012508e2019-07-22 18:30:40 -0700603 Partition* p = builder->AddPartition(
604 partition_name_suffix, group_name_suffix, LP_PARTITION_ATTR_READONLY);
605 if (!p) {
606 LOG(ERROR) << "Cannot add partition " << partition_name_suffix
607 << " to group " << group_name_suffix;
608 return false;
609 }
Yifan Hong13d41cb2019-09-16 13:18:22 -0700610 if (!builder->ResizePartition(p, partition_size)) {
Yifan Hong012508e2019-07-22 18:30:40 -0700611 LOG(ERROR) << "Cannot resize partition " << partition_name_suffix
Yifan Hong13d41cb2019-09-16 13:18:22 -0700612 << " to size " << partition_size << ". Not enough space?";
Yifan Hong012508e2019-07-22 18:30:40 -0700613 return false;
614 }
615 LOG(INFO) << "Added partition " << partition_name_suffix << " to group "
Yifan Hong13d41cb2019-09-16 13:18:22 -0700616 << group_name_suffix << " with size " << partition_size;
Yifan Hong012508e2019-07-22 18:30:40 -0700617 }
618 }
619
620 return true;
621}
622
Yifan Honga33bca42019-09-03 20:29:45 -0700623bool DynamicPartitionControlAndroid::FinishUpdate() {
Yifan Hongbae27842019-10-24 16:56:12 -0700624 if (snapshot_->GetUpdateState() == UpdateState::Initiated) {
Yifan Hongf0f4a912019-09-26 17:51:33 -0700625 LOG(INFO) << "Snapshot writes are done.";
626 return snapshot_->FinishedSnapshotWrites();
627 }
628 return true;
Yifan Honga33bca42019-09-03 20:29:45 -0700629}
630
Yifan Hong3a1a5612019-11-05 16:34:32 -0800631bool DynamicPartitionControlAndroid::GetPartitionDevice(
632 const std::string& partition_name,
633 uint32_t slot,
634 uint32_t current_slot,
635 std::string* device) {
636 const auto& partition_name_suffix =
637 partition_name + SlotSuffixForSlotNumber(slot);
638 std::string device_dir_str;
639 TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
640 base::FilePath device_dir(device_dir_str);
641
642 // When looking up target partition devices, treat them as static if the
643 // current payload doesn't encode them as dynamic partitions. This may happen
644 // when applying a retrofit update on top of a dynamic-partitions-enabled
645 // build.
646 if (GetDynamicPartitionsFeatureFlag().IsEnabled() &&
647 (slot == current_slot || is_target_dynamic_)) {
648 switch (GetDynamicPartitionDevice(
649 device_dir, partition_name_suffix, slot, current_slot, device)) {
650 case DynamicPartitionDeviceStatus::SUCCESS:
651 return true;
652 case DynamicPartitionDeviceStatus::TRY_STATIC:
653 break;
654 case DynamicPartitionDeviceStatus::ERROR: // fallthrough
655 default:
656 return false;
657 }
658 }
659 base::FilePath path = device_dir.Append(partition_name_suffix);
660 if (!DeviceExists(path.value())) {
661 LOG(ERROR) << "Device file " << path.value() << " does not exist.";
662 return false;
663 }
664
665 *device = path.value();
666 return true;
667}
668
669bool DynamicPartitionControlAndroid::IsSuperBlockDevice(
670 const base::FilePath& device_dir,
671 uint32_t current_slot,
672 const std::string& partition_name_suffix) {
673 std::string source_device =
674 device_dir.Append(GetSuperPartitionName(current_slot)).value();
675 auto source_metadata = LoadMetadataBuilder(source_device, current_slot);
676 return source_metadata->HasBlockDevice(partition_name_suffix);
677}
678
679DynamicPartitionControlAndroid::DynamicPartitionDeviceStatus
680DynamicPartitionControlAndroid::GetDynamicPartitionDevice(
681 const base::FilePath& device_dir,
682 const std::string& partition_name_suffix,
683 uint32_t slot,
684 uint32_t current_slot,
685 std::string* device) {
686 std::string super_device =
687 device_dir.Append(GetSuperPartitionName(slot)).value();
688
689 auto builder = LoadMetadataBuilder(super_device, slot);
690 if (builder == nullptr) {
691 LOG(ERROR) << "No metadata in slot "
692 << BootControlInterface::SlotName(slot);
693 return DynamicPartitionDeviceStatus::ERROR;
694 }
695 if (builder->FindPartition(partition_name_suffix) == nullptr) {
696 LOG(INFO) << partition_name_suffix
697 << " is not in super partition metadata.";
698
699 if (IsSuperBlockDevice(device_dir, current_slot, partition_name_suffix)) {
700 LOG(ERROR) << "The static partition " << partition_name_suffix
701 << " is a block device for current metadata."
702 << "It cannot be used as a logical partition.";
703 return DynamicPartitionDeviceStatus::ERROR;
704 }
705
706 return DynamicPartitionDeviceStatus::TRY_STATIC;
707 }
708
709 if (slot == current_slot) {
710 if (GetState(partition_name_suffix) != DmDeviceState::ACTIVE) {
711 LOG(WARNING) << partition_name_suffix << " is at current slot but it is "
712 << "not mapped. Now try to map it.";
713 } else {
714 if (GetDmDevicePathByName(partition_name_suffix, device)) {
715 LOG(INFO) << partition_name_suffix
716 << " is mapped on device mapper: " << *device;
717 return DynamicPartitionDeviceStatus::SUCCESS;
718 }
719 LOG(ERROR) << partition_name_suffix << "is mapped but path is unknown.";
720 return DynamicPartitionDeviceStatus::ERROR;
721 }
722 }
723
724 bool force_writable = slot != current_slot;
725 if (MapPartitionOnDeviceMapper(
726 super_device, partition_name_suffix, slot, force_writable, device)) {
727 return DynamicPartitionDeviceStatus::SUCCESS;
728 }
729 return DynamicPartitionDeviceStatus::ERROR;
730}
731
Yifan Hong6eec9952019-12-04 13:12:01 -0800732void DynamicPartitionControlAndroid::set_fake_mapped_devices(
733 const std::set<std::string>& fake) {
734 mapped_devices_ = fake;
735}
736
Yifan Hong2257ee12020-01-13 18:33:00 -0800737ErrorCode DynamicPartitionControlAndroid::CleanupSuccessfulUpdate() {
738 // Already reboot into new boot. Clean up.
739 if (!GetVirtualAbFeatureFlag().IsEnabled()) {
740 return ErrorCode::kSuccess;
741 }
742 auto ret = snapshot_->WaitForMerge();
743 if (ret.is_ok()) {
744 return ErrorCode::kSuccess;
745 }
746 if (ret.error_code() == Return::ErrorCode::NEEDS_REBOOT) {
747 return ErrorCode::kError;
748 }
749 return ErrorCode::kDeviceCorrupted;
750}
751
Yifan Hongbae27842019-10-24 16:56:12 -0700752bool DynamicPartitionControlAndroid::IsRecovery() {
753 return kIsRecovery;
754}
755
756static bool IsIncrementalUpdate(const DeltaArchiveManifest& manifest) {
757 const auto& partitions = manifest.partitions();
758 return std::any_of(partitions.begin(), partitions.end(), [](const auto& p) {
759 return p.has_old_partition_info();
760 });
761}
762
763bool DynamicPartitionControlAndroid::DeleteSourcePartitions(
764 MetadataBuilder* builder,
765 uint32_t source_slot,
766 const DeltaArchiveManifest& manifest) {
767 TEST_AND_RETURN_FALSE(IsRecovery());
768
769 if (IsIncrementalUpdate(manifest)) {
770 LOG(ERROR) << "Cannot sideload incremental OTA because snapshots cannot "
771 << "be created.";
772 if (GetVirtualAbFeatureFlag().IsLaunch()) {
773 LOG(ERROR) << "Sideloading incremental updates on devices launches "
774 << " Virtual A/B is not supported.";
775 }
776 return false;
777 }
778
779 LOG(INFO) << "Will overwrite existing partitions. Slot "
780 << BootControlInterface::SlotName(source_slot)
781 << "may be unbootable until update finishes!";
782 const std::string source_suffix = SlotSuffixForSlotNumber(source_slot);
783 DeleteGroupsWithSuffix(builder, source_suffix);
784
785 return true;
786}
787
Yifan Hong537802d2018-08-15 13:15:42 -0700788} // namespace chromeos_update_engine