diff options
author | Yifan Hong <elsk@google.com> | 2020-04-09 01:49:17 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-04-09 01:49:17 +0000 |
commit | ce0bac1bf6d850c34c3a7c2d4b6acee1c0069ae4 (patch) | |
tree | 19c54995f1ca89ff9cd2f04e50712eea539e9ab1 | |
parent | 4ff28368e67de59c7b7860e8fe6199bdb55107f2 (diff) | |
parent | 845b9eb98b89e9206eae5c5b973521ea0e98358a (diff) | |
download | update_engine-ce0bac1bf6d850c34c3a7c2d4b6acee1c0069ae4.tar.gz |
Merge "Allow to skip mounting metadata in recovery." into rvc-dev am: 9764582021 am: 845b9eb98b
Change-Id: I2e5f971ab4cce40cc28a2767e028d49ebebd98fa
-rw-r--r-- | cleanup_previous_update_action.cc | 5 | ||||
-rw-r--r-- | dynamic_partition_control_android.cc | 86 | ||||
-rw-r--r-- | dynamic_partition_control_android.h | 22 |
3 files changed, 98 insertions, 15 deletions
diff --git a/cleanup_previous_update_action.cc b/cleanup_previous_update_action.cc index ee689472..26cc6be9 100644 --- a/cleanup_previous_update_action.cc +++ b/cleanup_previous_update_action.cc @@ -160,7 +160,10 @@ void CleanupPreviousUpdateAction::CheckSlotMarkedSuccessfulOrSchedule() { if (metadata_device_ == nullptr) { LOG(ERROR) << "Failed to mount /metadata."; - processor_->ActionComplete(this, ErrorCode::kError); + // If metadata is erased but not formatted, it is possible to not mount + // it in recovery. It is safe to skip CleanupPreviousUpdateAction. + processor_->ActionComplete( + this, kIsRecovery ? ErrorCode::kSuccess : ErrorCode::kError); return; } diff --git a/dynamic_partition_control_android.cc b/dynamic_partition_control_android.cc index 1e92f45b..a310f209 100644 --- a/dynamic_partition_control_android.cc +++ b/dynamic_partition_control_android.cc @@ -152,10 +152,12 @@ bool DynamicPartitionControlAndroid::MapPartitionInternal( }; bool success = false; if (GetVirtualAbFeatureFlag().IsEnabled() && target_supports_snapshot_ && - force_writable) { + force_writable && ExpectMetadataMounted()) { // Only target partitions are mapped with force_writable. On Virtual // A/B devices, target partitions may overlap with source partitions, so // they must be mapped with snapshot. + // One exception is when /metadata is not mounted. Fallback to + // CreateLogicalPartition as snapshots are not created in the first place. params.timeout_ms = kMapSnapshotTimeout; success = snapshot_->MapUpdateSnapshot(params, path); } else { @@ -232,8 +234,11 @@ bool DynamicPartitionControlAndroid::UnmapPartitionOnDeviceMapper( // On a Virtual A/B device, |target_partition_name| may be a leftover from // a paused update. Clean up any underlying devices. - if (GetVirtualAbFeatureFlag().IsEnabled()) { + if (ExpectMetadataMounted()) { success &= snapshot_->UnmapUpdateSnapshot(target_partition_name); + } else { + LOG(INFO) << "Skip UnmapUpdateSnapshot(" << target_partition_name + << ") because metadata is not mounted"; } if (!success) { @@ -405,10 +410,10 @@ bool DynamicPartitionControlAndroid::PreparePartitionsForUpdate( << "run adb enable-verity to deactivate if required and try again."; } - if (GetVirtualAbFeatureFlag().IsEnabled() && metadata_device_ == nullptr) { - metadata_device_ = snapshot_->EnsureMetadataMounted(); - TEST_AND_RETURN_FALSE(metadata_device_ != nullptr); - } + // If metadata is erased but not formatted, it is possible to not mount + // it in recovery. It is acceptable to skip mounting and choose fallback path + // (PrepareDynamicPartitionsForUpdate) when sideloading full OTAs. + TEST_AND_RETURN_FALSE(EnsureMetadataMounted() || IsRecovery()); if (update) { TEST_AND_RETURN_FALSE(EraseSystemOtherAvbFooter(source_slot, target_slot)); @@ -469,9 +474,18 @@ bool DynamicPartitionControlAndroid::PreparePartitionsForUpdate( << "snapshots."; } - if (!snapshot_->CancelUpdate()) { - LOG(ERROR) << "Cannot cancel previous update."; - return false; + // In recovery, if /metadata is not mounted, it is likely that metadata + // partition is erased and not formatted yet. After sideloading, when + // rebooting into the new version, init will erase metadata partition, + // hence the failure of CancelUpdate() can be ignored here. + // However, if metadata is mounted and CancelUpdate fails, sideloading + // should not proceed because during next boot, snapshots will overlay on + // the devices incorrectly. + if (ExpectMetadataMounted()) { + TEST_AND_RETURN_FALSE(snapshot_->CancelUpdate()); + } else { + LOG(INFO) << "Skip canceling previous update because metadata is not " + << "mounted"; } } @@ -632,6 +646,9 @@ bool DynamicPartitionControlAndroid::GetSystemOtherPath( // Delete any pre-existing device with name |partition_name_suffix| and // also remove it from |mapped_devices_|. + // In recovery, metadata might not be mounted, and + // UnmapPartitionOnDeviceMapper might fail. However, + // it is unusual that system_other has already been mapped. Hence, just skip. TEST_AND_RETURN_FALSE(UnmapPartitionOnDeviceMapper(partition_name_suffix)); // Use CreateLogicalPartition directly to avoid mapping with existing // snapshots. @@ -668,6 +685,10 @@ bool DynamicPartitionControlAndroid::EraseSystemOtherAvbFooter( // Delete |partition_name_suffix| from device mapper and from // |mapped_devices_| again so that it does not interfere with update process. + // In recovery, metadata might not be mounted, and + // UnmapPartitionOnDeviceMapper might fail. However, DestroyLogicalPartition + // should be called. If DestroyLogicalPartition does fail, it is still okay + // to skip the error here and let Prepare*() fail later. if (should_unmap) { TEST_AND_RETURN_FALSE(UnmapPartitionOnDeviceMapper(partition_name_suffix)); } @@ -726,6 +747,7 @@ bool DynamicPartitionControlAndroid::PrepareSnapshotPartitionsForUpdate( uint32_t target_slot, const DeltaArchiveManifest& manifest, uint64_t* required_size) { + TEST_AND_RETURN_FALSE(ExpectMetadataMounted()); if (!snapshot_->BeginUpdate()) { LOG(ERROR) << "Cannot begin new update."; return false; @@ -829,10 +851,14 @@ bool DynamicPartitionControlAndroid::UpdatePartitionMetadata( } bool DynamicPartitionControlAndroid::FinishUpdate(bool powerwash_required) { - if (GetVirtualAbFeatureFlag().IsEnabled() && - snapshot_->GetUpdateState() == UpdateState::Initiated) { - LOG(INFO) << "Snapshot writes are done."; - return snapshot_->FinishedSnapshotWrites(powerwash_required); + if (ExpectMetadataMounted()) { + if (snapshot_->GetUpdateState() == UpdateState::Initiated) { + LOG(INFO) << "Snapshot writes are done."; + return snapshot_->FinishedSnapshotWrites(powerwash_required); + } + } else { + LOG(INFO) << "Skip FinishedSnapshotWrites() because /metadata is not " + << "mounted"; } return true; } @@ -1006,9 +1032,41 @@ bool DynamicPartitionControlAndroid::ResetUpdate(PrefsInterface* prefs) { TEST_AND_RETURN_FALSE(DeltaPerformer::ResetUpdateProgress( prefs, false /* quick */, false /* skip dynamic partitions metadata */)); - TEST_AND_RETURN_FALSE(snapshot_->CancelUpdate()); + if (ExpectMetadataMounted()) { + TEST_AND_RETURN_FALSE(snapshot_->CancelUpdate()); + } else { + LOG(INFO) << "Skip cancelling update in ResetUpdate because /metadata is " + << "not mounted"; + } return true; } +bool DynamicPartitionControlAndroid::ExpectMetadataMounted() { + // No need to mount metadata for non-Virtual A/B devices. + if (!GetVirtualAbFeatureFlag().IsEnabled()) { + return false; + } + // Intentionally not checking |metadata_device_| in Android mode. + // /metadata should always be mounted in Android mode. If it isn't, let caller + // fails when calling into SnapshotManager. + if (!IsRecovery()) { + return true; + } + // In recovery mode, explicitly check |metadata_device_|. + return metadata_device_ != nullptr; +} + +bool DynamicPartitionControlAndroid::EnsureMetadataMounted() { + // No need to mount metadata for non-Virtual A/B devices. + if (!GetVirtualAbFeatureFlag().IsEnabled()) { + return true; + } + + if (metadata_device_ == nullptr) { + metadata_device_ = snapshot_->EnsureMetadataMounted(); + } + return metadata_device_ != nullptr; +} + } // namespace chromeos_update_engine diff --git a/dynamic_partition_control_android.h b/dynamic_partition_control_android.h index 9dcdcf1a..8ad75933 100644 --- a/dynamic_partition_control_android.h +++ b/dynamic_partition_control_android.h @@ -233,6 +233,28 @@ class DynamicPartitionControlAndroid : public DynamicPartitionControlInterface { uint32_t source_slot, const DeltaArchiveManifest& manifest); + // Returns true if metadata is expected to be mounted, false otherwise. + // Note that it returns false on non-Virtual A/B devices. + // + // Almost all functions of SnapshotManager depends on metadata being mounted. + // - In Android mode for Virtual A/B devices, assume it is mounted. If not, + // let caller fails when calling into SnapshotManager. + // - In recovery for Virtual A/B devices, it is possible that metadata is not + // formatted, hence it cannot be mounted. Caller should not call into + // SnapshotManager. + // - On non-Virtual A/B devices, updates do not depend on metadata partition. + // Caller should not call into SnapshotManager. + // + // This function does NOT mount metadata partition. Use EnsureMetadataMounted + // to mount metadata partition. + bool ExpectMetadataMounted(); + + // Ensure /metadata is mounted. Returns true if successful, false otherwise. + // + // Note that this function returns true on non-Virtual A/B devices without + // doing anything. + bool EnsureMetadataMounted(); + std::set<std::string> mapped_devices_; const FeatureFlag dynamic_partitions_; const FeatureFlag virtual_ab_; |