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