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