aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYifan Hong <elsk@google.com>2020-04-09 01:49:17 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-04-09 01:49:17 +0000
commitce0bac1bf6d850c34c3a7c2d4b6acee1c0069ae4 (patch)
tree19c54995f1ca89ff9cd2f04e50712eea539e9ab1
parent4ff28368e67de59c7b7860e8fe6199bdb55107f2 (diff)
parent845b9eb98b89e9206eae5c5b973521ea0e98358a (diff)
downloadupdate_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.cc5
-rw-r--r--dynamic_partition_control_android.cc86
-rw-r--r--dynamic_partition_control_android.h22
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_;