Loading fs_mgr/libsnapshot/device_info.cpp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,7 @@ namespace android { namespace snapshot { namespace snapshot { #ifdef LIBSNAPSHOT_USE_HAL #ifdef LIBSNAPSHOT_USE_HAL using android::hardware::boot::V1_0::BoolResult; using android::hardware::boot::V1_0::CommandResult; using android::hardware::boot::V1_0::CommandResult; #endif #endif Loading fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +6 −2 Original line number Original line Diff line number Diff line Loading @@ -174,6 +174,7 @@ class SnapshotManager final { // See InitiateMerge() and ProcessUpdateState() for details. // See InitiateMerge() and ProcessUpdateState() for details. // Returns: // Returns: // - None if no merge to initiate // - None if no merge to initiate // - Unverified if called on the source slot // - MergeCompleted if merge is completed // - MergeCompleted if merge is completed // - other states indicating an error has occurred // - other states indicating an error has occurred UpdateState InitiateMergeAndWait(); UpdateState InitiateMergeAndWait(); Loading Loading @@ -273,6 +274,7 @@ class SnapshotManager final { FRIEND_TEST(SnapshotTest, UpdateBootControlHal); FRIEND_TEST(SnapshotTest, UpdateBootControlHal); FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback); FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback); FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery); FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery); FRIEND_TEST(SnapshotUpdateTest, FullUpdateFlow); FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow); FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow); FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery); FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery); FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow); FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow); Loading Loading @@ -374,7 +376,7 @@ class SnapshotManager final { bool HandleCancelledUpdate(LockedFile* lock); bool HandleCancelledUpdate(LockedFile* lock); // Helper for HandleCancelledUpdate. Assumes booting from new slot. // Helper for HandleCancelledUpdate. Assumes booting from new slot. bool HandleCancelledUpdateOnNewSlot(LockedFile* lock); bool AreAllSnapshotsCancelled(LockedFile* lock); // Remove artifacts created by the update process, such as snapshots, and // Remove artifacts created by the update process, such as snapshots, and // set the update state to None. // set the update state to None. Loading Loading @@ -439,7 +441,7 @@ class SnapshotManager final { std::string GetSnapshotStatusFilePath(const std::string& name); std::string GetSnapshotStatusFilePath(const std::string& name); std::string GetSnapshotBootIndicatorPath(); std::string GetSnapshotBootIndicatorPath(); void RemoveSnapshotBootIndicator(); std::string GetRollbackIndicatorPath(); // Return the name of the device holding the "snapshot" or "snapshot-merge" // Return the name of the device holding the "snapshot" or "snapshot-merge" // target. This may not be the final device presented via MapSnapshot(), if // target. This may not be the final device presented via MapSnapshot(), if Loading Loading @@ -503,6 +505,8 @@ class SnapshotManager final { friend std::ostream& operator<<(std::ostream& os, SnapshotManager::Slot slot); friend std::ostream& operator<<(std::ostream& os, SnapshotManager::Slot slot); Slot GetCurrentSlot(); Slot GetCurrentSlot(); std::string ReadUpdateSourceSlotSuffix(); std::string gsid_dir_; std::string gsid_dir_; std::string metadata_dir_; std::string metadata_dir_; std::unique_ptr<IDeviceInfo> device_; std::unique_ptr<IDeviceInfo> device_; Loading fs_mgr/libsnapshot/snapshot.cpp +85 −45 Original line number Original line Diff line number Diff line Loading @@ -188,11 +188,19 @@ bool SnapshotManager::TryCancelUpdate(bool* needs_merge) { return true; return true; } } SnapshotManager::Slot SnapshotManager::GetCurrentSlot() { std::string SnapshotManager::ReadUpdateSourceSlotSuffix() { auto boot_file = GetSnapshotBootIndicatorPath(); auto boot_file = GetSnapshotBootIndicatorPath(); std::string contents; std::string contents; if (!android::base::ReadFileToString(boot_file, &contents)) { if (!android::base::ReadFileToString(boot_file, &contents)) { PLOG(WARNING) << "Cannot read " << boot_file; PLOG(WARNING) << "Cannot read " << boot_file; return {}; } return contents; } SnapshotManager::Slot SnapshotManager::GetCurrentSlot() { auto contents = ReadUpdateSourceSlotSuffix(); if (contents.empty()) { return Slot::Unknown; return Slot::Unknown; } } if (device_->GetSlotSuffix() == contents) { if (device_->GetSlotSuffix() == contents) { Loading @@ -201,6 +209,15 @@ SnapshotManager::Slot SnapshotManager::GetCurrentSlot() { return Slot::Target; return Slot::Target; } } static bool RemoveFileIfExists(const std::string& path) { std::string message; if (!android::base::RemoveFileIfExists(path, &message)) { LOG(ERROR) << "Remove failed: " << path << ": " << message; return false; } return true; } bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) { bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) { LOG(INFO) << "Removing all update state."; LOG(INFO) << "Removing all update state."; Loading @@ -223,7 +240,13 @@ bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) { return false; return false; } } RemoveSnapshotBootIndicator(); // It's okay if these fail - first-stage init performs a deeper check after // reading the indicator file, so it's not a problem if it still exists // after the update completes. std::vector<std::string> files = {GetSnapshotBootIndicatorPath(), GetRollbackIndicatorPath()}; for (const auto& file : files) { RemoveFileIfExists(file); } // If this fails, we'll keep trying to remove the update state (as the // If this fails, we'll keep trying to remove the update state (as the // device reboots or starts a new update) until it finally succeeds. // device reboots or starts a new update) until it finally succeeds. Loading @@ -250,6 +273,13 @@ bool SnapshotManager::FinishedSnapshotWrites() { return false; return false; } } // This file is written on boot to detect whether a rollback occurred. It // MUST NOT exist before rebooting, otherwise, we're at risk of deleting // snapshots too early. if (!RemoveFileIfExists(GetRollbackIndicatorPath())) { return false; } // This file acts as both a quick indicator for init (it can use access(2) // This file acts as both a quick indicator for init (it can use access(2) // to decide how to do first-stage mounts), and it stores the old slot, so // to decide how to do first-stage mounts), and it stores the old slot, so // we can tell whether or not we performed a rollback. // we can tell whether or not we performed a rollback. Loading Loading @@ -966,14 +996,8 @@ std::string SnapshotManager::GetSnapshotBootIndicatorPath() { return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath); return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath); } } void SnapshotManager::RemoveSnapshotBootIndicator() { std::string SnapshotManager::GetRollbackIndicatorPath() { // It's okay if this fails - first-stage init performs a deeper check after return metadata_dir_ + "/rollback-indicator"; // reading the indicator file, so it's not a problem if it still exists // after the update completes. auto boot_file = GetSnapshotBootIndicatorPath(); if (unlink(boot_file.c_str()) == -1 && errno != ENOENT) { PLOG(ERROR) << "unlink " << boot_file; } } } void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) { void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) { Loading Loading @@ -1144,27 +1168,20 @@ bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock) { if (slot == Slot::Unknown) { if (slot == Slot::Unknown) { return false; return false; } } if (slot == Slot::Target) { // We're booted into the target slot, which means we just rebooted // after applying the update. if (!HandleCancelledUpdateOnNewSlot(lock)) { return false; } } // The only way we can get here is if: // If all snapshots were reflashed, then cancel the entire update. // (1) The device rolled back to the previous slot. if (AreAllSnapshotsCancelled(lock)) { // (2) This function was called prematurely before rebooting the device. // (3) fastboot set_active was used. // (4) The device updates to the new slot but re-flashed *all* partitions // in the new slot. // // In any case, delete the snapshots. It may be worth using the boot_control // HAL to differentiate case (2). RemoveAllUpdateState(lock); RemoveAllUpdateState(lock); return true; return true; } } // This unverified update might be rolled back, or it might not (b/147347110 // comment #77). Take no action, as update_engine is responsible for deciding // whether to cancel. LOG(ERROR) << "Update state is being processed before reboot, taking no action."; return false; } std::unique_ptr<LpMetadata> SnapshotManager::ReadCurrentMetadata() { std::unique_ptr<LpMetadata> SnapshotManager::ReadCurrentMetadata() { const auto& opener = device_->GetPartitionOpener(); const auto& opener = device_->GetPartitionOpener(); uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix()); uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix()); Loading @@ -1187,7 +1204,7 @@ SnapshotManager::MetadataPartitionState SnapshotManager::GetMetadataPartitionSta return MetadataPartitionState::Flashed; return MetadataPartitionState::Flashed; } } bool SnapshotManager::HandleCancelledUpdateOnNewSlot(LockedFile* lock) { bool SnapshotManager::AreAllSnapshotsCancelled(LockedFile* lock) { std::vector<std::string> snapshots; std::vector<std::string> snapshots; if (!ListSnapshots(lock, &snapshots)) { if (!ListSnapshots(lock, &snapshots)) { LOG(WARNING) << "Failed to list snapshots to determine whether device has been flashed " LOG(WARNING) << "Failed to list snapshots to determine whether device has been flashed " Loading @@ -1196,35 +1213,45 @@ bool SnapshotManager::HandleCancelledUpdateOnNewSlot(LockedFile* lock) { return true; return true; } } auto source_slot_suffix = ReadUpdateSourceSlotSuffix(); if (source_slot_suffix.empty()) { return false; } uint32_t source_slot = SlotNumberForSlotSuffix(source_slot_suffix); uint32_t target_slot = (source_slot == 0) ? 1 : 0; // Attempt to detect re-flashing on each partition. // Attempt to detect re-flashing on each partition. // - If all partitions are re-flashed, we can proceed to cancel the whole update. // - If all partitions are re-flashed, we can proceed to cancel the whole update. // - If only some of the partitions are re-flashed, snapshots for re-flashed partitions are // - If only some of the partitions are re-flashed, snapshots for re-flashed partitions are // deleted. Caller is responsible for merging the rest of the snapshots. // deleted. Caller is responsible for merging the rest of the snapshots. // - If none of the partitions are re-flashed, caller is responsible for merging the snapshots. // - If none of the partitions are re-flashed, caller is responsible for merging the snapshots. auto metadata = ReadCurrentMetadata(); // if (!metadata) return false; // Note that we use target slot metadata, since if an OTA has been applied bool all_snapshot_cancelled = true; // to the target slot, we can detect the UPDATED flag. Any kind of flash // operation against dynamic partitions ensures that all copies of the // metadata are in sync, so flashing all partitions on the source slot will // remove the UPDATED flag on the target slot as well. const auto& opener = device_->GetPartitionOpener(); auto super_device = device_->GetSuperDevice(target_slot); auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, target_slot); if (!metadata) { return false; } bool all_snapshots_cancelled = true; for (const auto& snapshot_name : snapshots) { for (const auto& snapshot_name : snapshots) { if (GetMetadataPartitionState(*metadata, snapshot_name) == if (GetMetadataPartitionState(*metadata, snapshot_name) == MetadataPartitionState::Updated) { MetadataPartitionState::Updated) { LOG(WARNING) << "Cannot cancel update because snapshot" << snapshot_name all_snapshots_cancelled = false; << " is in use."; all_snapshot_cancelled = false; continue; continue; } } // Delete snapshots for partitions that are re-flashed after the update. // Delete snapshots for partitions that are re-flashed after the update. LOG(INFO) << "Detected re-flashing of partition " << snapshot_name << "."; LOG(WARNING) << "Detected re-flashing of partition " << snapshot_name << "."; if (!DeleteSnapshot(lock, snapshot_name)) { // This is an error, but it is okay to leave the snapshot in the short term. // However, if all_snapshot_cancelled == false after exiting the loop, caller may // initiate merge for this unused snapshot, which is likely to fail. LOG(WARNING) << "Failed to delete snapshot for re-flashed partition " << snapshot_name; } } if (all_snapshots_cancelled) { LOG(WARNING) << "All partitions are re-flashed after update, removing all update states."; } } if (!all_snapshot_cancelled) return false; return all_snapshots_cancelled; LOG(INFO) << "All partitions are re-flashed after update, removing all update states."; return true; } } bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) { bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) { Loading Loading @@ -1344,7 +1371,16 @@ bool SnapshotManager::NeedSnapshotsInFirstStageMount() { // the reason be clearer? Because the indicator file still exists, and // the reason be clearer? Because the indicator file still exists, and // if this was FATAL, reverting to the old slot would be broken. // if this was FATAL, reverting to the old slot would be broken. auto slot = GetCurrentSlot(); auto slot = GetCurrentSlot(); if (slot != Slot::Target) { if (slot != Slot::Target) { if (slot == Slot::Source && !device_->IsRecovery()) { // Device is rebooting into the original slot, so mark this as a // rollback. auto path = GetRollbackIndicatorPath(); if (!android::base::WriteStringToFile("1", path)) { PLOG(ERROR) << "Unable to write rollback indicator: " << path; } } LOG(INFO) << "Not booting from new slot. Will not mount snapshots."; LOG(INFO) << "Not booting from new slot. Will not mount snapshots."; return false; return false; } } Loading Loading @@ -2370,6 +2406,10 @@ UpdateState SnapshotManager::InitiateMergeAndWait() { return state; return state; } } if (state == UpdateState::Unverified) { if (state == UpdateState::Unverified) { if (GetCurrentSlot() != Slot::Target) { LOG(INFO) << "Cannot merge until device reboots."; return state; } if (!InitiateMerge()) { if (!InitiateMerge()) { LOG(ERROR) << "Failed to initiate merge."; LOG(ERROR) << "Failed to initiate merge."; return state; return state; Loading fs_mgr/libsnapshot/snapshot_test.cpp +9 −1 Original line number Original line Diff line number Diff line Loading @@ -447,6 +447,9 @@ TEST_F(SnapshotTest, FirstStageMountAfterRollback) { auto sm = SnapshotManager::NewForFirstStageMount(info); auto sm = SnapshotManager::NewForFirstStageMount(info); ASSERT_NE(sm, nullptr); ASSERT_NE(sm, nullptr); ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount()); ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount()); auto indicator = sm->GetRollbackIndicatorPath(); ASSERT_EQ(access(indicator.c_str(), R_OK), 0); } } TEST_F(SnapshotTest, Merge) { TEST_F(SnapshotTest, Merge) { Loading Loading @@ -1015,6 +1018,9 @@ TEST_F(SnapshotUpdateTest, FullUpdateFlow) { ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); auto indicator = sm->GetRollbackIndicatorPath(); ASSERT_NE(access(indicator.c_str(), R_OK), 0); // Check that the target partitions have the same content. // Check that the target partitions have the same content. for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { ASSERT_TRUE(IsPartitionUnchanged(name)); ASSERT_TRUE(IsPartitionUnchanged(name)); Loading Loading @@ -1681,9 +1687,11 @@ TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) { ASSERT_NE(nullptr, flashed_builder->FindPartition("prd" + flashed_slot_suffix)); ASSERT_NE(nullptr, flashed_builder->FindPartition("prd" + flashed_slot_suffix)); flashed_builder->RemovePartition("prd" + flashed_slot_suffix); flashed_builder->RemovePartition("prd" + flashed_slot_suffix); // Note that fastbootd always updates the partition table of both slots. auto flashed_metadata = flashed_builder->Export(); auto flashed_metadata = flashed_builder->Export(); ASSERT_NE(nullptr, flashed_metadata); ASSERT_NE(nullptr, flashed_metadata); ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, flashed_slot)); ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, 0)); ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, 1)); std::string path; std::string path; for (const auto& name : {"sys", "vnd"}) { for (const auto& name : {"sys", "vnd"}) { Loading fs_mgr/libsnapshot/snapshotctl.cpp +3 −1 Original line number Original line Diff line number Diff line Loading @@ -112,7 +112,9 @@ bool MergeCmdHandler(int argc, char** argv) { auto state = SnapshotManager::New()->InitiateMergeAndWait(); auto state = SnapshotManager::New()->InitiateMergeAndWait(); if (state == UpdateState::None) { // We could wind up in the Unverified state if the device rolled back or // hasn't fully rebooted. Ignore this. if (state == UpdateState::None || state == UpdateState::Unverified) { return true; return true; } } if (state == UpdateState::MergeCompleted) { if (state == UpdateState::MergeCompleted) { Loading Loading
fs_mgr/libsnapshot/device_info.cpp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -22,6 +22,7 @@ namespace android { namespace snapshot { namespace snapshot { #ifdef LIBSNAPSHOT_USE_HAL #ifdef LIBSNAPSHOT_USE_HAL using android::hardware::boot::V1_0::BoolResult; using android::hardware::boot::V1_0::CommandResult; using android::hardware::boot::V1_0::CommandResult; #endif #endif Loading
fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +6 −2 Original line number Original line Diff line number Diff line Loading @@ -174,6 +174,7 @@ class SnapshotManager final { // See InitiateMerge() and ProcessUpdateState() for details. // See InitiateMerge() and ProcessUpdateState() for details. // Returns: // Returns: // - None if no merge to initiate // - None if no merge to initiate // - Unverified if called on the source slot // - MergeCompleted if merge is completed // - MergeCompleted if merge is completed // - other states indicating an error has occurred // - other states indicating an error has occurred UpdateState InitiateMergeAndWait(); UpdateState InitiateMergeAndWait(); Loading Loading @@ -273,6 +274,7 @@ class SnapshotManager final { FRIEND_TEST(SnapshotTest, UpdateBootControlHal); FRIEND_TEST(SnapshotTest, UpdateBootControlHal); FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback); FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback); FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery); FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery); FRIEND_TEST(SnapshotUpdateTest, FullUpdateFlow); FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow); FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow); FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery); FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery); FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow); FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow); Loading Loading @@ -374,7 +376,7 @@ class SnapshotManager final { bool HandleCancelledUpdate(LockedFile* lock); bool HandleCancelledUpdate(LockedFile* lock); // Helper for HandleCancelledUpdate. Assumes booting from new slot. // Helper for HandleCancelledUpdate. Assumes booting from new slot. bool HandleCancelledUpdateOnNewSlot(LockedFile* lock); bool AreAllSnapshotsCancelled(LockedFile* lock); // Remove artifacts created by the update process, such as snapshots, and // Remove artifacts created by the update process, such as snapshots, and // set the update state to None. // set the update state to None. Loading Loading @@ -439,7 +441,7 @@ class SnapshotManager final { std::string GetSnapshotStatusFilePath(const std::string& name); std::string GetSnapshotStatusFilePath(const std::string& name); std::string GetSnapshotBootIndicatorPath(); std::string GetSnapshotBootIndicatorPath(); void RemoveSnapshotBootIndicator(); std::string GetRollbackIndicatorPath(); // Return the name of the device holding the "snapshot" or "snapshot-merge" // Return the name of the device holding the "snapshot" or "snapshot-merge" // target. This may not be the final device presented via MapSnapshot(), if // target. This may not be the final device presented via MapSnapshot(), if Loading Loading @@ -503,6 +505,8 @@ class SnapshotManager final { friend std::ostream& operator<<(std::ostream& os, SnapshotManager::Slot slot); friend std::ostream& operator<<(std::ostream& os, SnapshotManager::Slot slot); Slot GetCurrentSlot(); Slot GetCurrentSlot(); std::string ReadUpdateSourceSlotSuffix(); std::string gsid_dir_; std::string gsid_dir_; std::string metadata_dir_; std::string metadata_dir_; std::unique_ptr<IDeviceInfo> device_; std::unique_ptr<IDeviceInfo> device_; Loading
fs_mgr/libsnapshot/snapshot.cpp +85 −45 Original line number Original line Diff line number Diff line Loading @@ -188,11 +188,19 @@ bool SnapshotManager::TryCancelUpdate(bool* needs_merge) { return true; return true; } } SnapshotManager::Slot SnapshotManager::GetCurrentSlot() { std::string SnapshotManager::ReadUpdateSourceSlotSuffix() { auto boot_file = GetSnapshotBootIndicatorPath(); auto boot_file = GetSnapshotBootIndicatorPath(); std::string contents; std::string contents; if (!android::base::ReadFileToString(boot_file, &contents)) { if (!android::base::ReadFileToString(boot_file, &contents)) { PLOG(WARNING) << "Cannot read " << boot_file; PLOG(WARNING) << "Cannot read " << boot_file; return {}; } return contents; } SnapshotManager::Slot SnapshotManager::GetCurrentSlot() { auto contents = ReadUpdateSourceSlotSuffix(); if (contents.empty()) { return Slot::Unknown; return Slot::Unknown; } } if (device_->GetSlotSuffix() == contents) { if (device_->GetSlotSuffix() == contents) { Loading @@ -201,6 +209,15 @@ SnapshotManager::Slot SnapshotManager::GetCurrentSlot() { return Slot::Target; return Slot::Target; } } static bool RemoveFileIfExists(const std::string& path) { std::string message; if (!android::base::RemoveFileIfExists(path, &message)) { LOG(ERROR) << "Remove failed: " << path << ": " << message; return false; } return true; } bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) { bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) { LOG(INFO) << "Removing all update state."; LOG(INFO) << "Removing all update state."; Loading @@ -223,7 +240,13 @@ bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) { return false; return false; } } RemoveSnapshotBootIndicator(); // It's okay if these fail - first-stage init performs a deeper check after // reading the indicator file, so it's not a problem if it still exists // after the update completes. std::vector<std::string> files = {GetSnapshotBootIndicatorPath(), GetRollbackIndicatorPath()}; for (const auto& file : files) { RemoveFileIfExists(file); } // If this fails, we'll keep trying to remove the update state (as the // If this fails, we'll keep trying to remove the update state (as the // device reboots or starts a new update) until it finally succeeds. // device reboots or starts a new update) until it finally succeeds. Loading @@ -250,6 +273,13 @@ bool SnapshotManager::FinishedSnapshotWrites() { return false; return false; } } // This file is written on boot to detect whether a rollback occurred. It // MUST NOT exist before rebooting, otherwise, we're at risk of deleting // snapshots too early. if (!RemoveFileIfExists(GetRollbackIndicatorPath())) { return false; } // This file acts as both a quick indicator for init (it can use access(2) // This file acts as both a quick indicator for init (it can use access(2) // to decide how to do first-stage mounts), and it stores the old slot, so // to decide how to do first-stage mounts), and it stores the old slot, so // we can tell whether or not we performed a rollback. // we can tell whether or not we performed a rollback. Loading Loading @@ -966,14 +996,8 @@ std::string SnapshotManager::GetSnapshotBootIndicatorPath() { return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath); return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath); } } void SnapshotManager::RemoveSnapshotBootIndicator() { std::string SnapshotManager::GetRollbackIndicatorPath() { // It's okay if this fails - first-stage init performs a deeper check after return metadata_dir_ + "/rollback-indicator"; // reading the indicator file, so it's not a problem if it still exists // after the update completes. auto boot_file = GetSnapshotBootIndicatorPath(); if (unlink(boot_file.c_str()) == -1 && errno != ENOENT) { PLOG(ERROR) << "unlink " << boot_file; } } } void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) { void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) { Loading Loading @@ -1144,27 +1168,20 @@ bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock) { if (slot == Slot::Unknown) { if (slot == Slot::Unknown) { return false; return false; } } if (slot == Slot::Target) { // We're booted into the target slot, which means we just rebooted // after applying the update. if (!HandleCancelledUpdateOnNewSlot(lock)) { return false; } } // The only way we can get here is if: // If all snapshots were reflashed, then cancel the entire update. // (1) The device rolled back to the previous slot. if (AreAllSnapshotsCancelled(lock)) { // (2) This function was called prematurely before rebooting the device. // (3) fastboot set_active was used. // (4) The device updates to the new slot but re-flashed *all* partitions // in the new slot. // // In any case, delete the snapshots. It may be worth using the boot_control // HAL to differentiate case (2). RemoveAllUpdateState(lock); RemoveAllUpdateState(lock); return true; return true; } } // This unverified update might be rolled back, or it might not (b/147347110 // comment #77). Take no action, as update_engine is responsible for deciding // whether to cancel. LOG(ERROR) << "Update state is being processed before reboot, taking no action."; return false; } std::unique_ptr<LpMetadata> SnapshotManager::ReadCurrentMetadata() { std::unique_ptr<LpMetadata> SnapshotManager::ReadCurrentMetadata() { const auto& opener = device_->GetPartitionOpener(); const auto& opener = device_->GetPartitionOpener(); uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix()); uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix()); Loading @@ -1187,7 +1204,7 @@ SnapshotManager::MetadataPartitionState SnapshotManager::GetMetadataPartitionSta return MetadataPartitionState::Flashed; return MetadataPartitionState::Flashed; } } bool SnapshotManager::HandleCancelledUpdateOnNewSlot(LockedFile* lock) { bool SnapshotManager::AreAllSnapshotsCancelled(LockedFile* lock) { std::vector<std::string> snapshots; std::vector<std::string> snapshots; if (!ListSnapshots(lock, &snapshots)) { if (!ListSnapshots(lock, &snapshots)) { LOG(WARNING) << "Failed to list snapshots to determine whether device has been flashed " LOG(WARNING) << "Failed to list snapshots to determine whether device has been flashed " Loading @@ -1196,35 +1213,45 @@ bool SnapshotManager::HandleCancelledUpdateOnNewSlot(LockedFile* lock) { return true; return true; } } auto source_slot_suffix = ReadUpdateSourceSlotSuffix(); if (source_slot_suffix.empty()) { return false; } uint32_t source_slot = SlotNumberForSlotSuffix(source_slot_suffix); uint32_t target_slot = (source_slot == 0) ? 1 : 0; // Attempt to detect re-flashing on each partition. // Attempt to detect re-flashing on each partition. // - If all partitions are re-flashed, we can proceed to cancel the whole update. // - If all partitions are re-flashed, we can proceed to cancel the whole update. // - If only some of the partitions are re-flashed, snapshots for re-flashed partitions are // - If only some of the partitions are re-flashed, snapshots for re-flashed partitions are // deleted. Caller is responsible for merging the rest of the snapshots. // deleted. Caller is responsible for merging the rest of the snapshots. // - If none of the partitions are re-flashed, caller is responsible for merging the snapshots. // - If none of the partitions are re-flashed, caller is responsible for merging the snapshots. auto metadata = ReadCurrentMetadata(); // if (!metadata) return false; // Note that we use target slot metadata, since if an OTA has been applied bool all_snapshot_cancelled = true; // to the target slot, we can detect the UPDATED flag. Any kind of flash // operation against dynamic partitions ensures that all copies of the // metadata are in sync, so flashing all partitions on the source slot will // remove the UPDATED flag on the target slot as well. const auto& opener = device_->GetPartitionOpener(); auto super_device = device_->GetSuperDevice(target_slot); auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, target_slot); if (!metadata) { return false; } bool all_snapshots_cancelled = true; for (const auto& snapshot_name : snapshots) { for (const auto& snapshot_name : snapshots) { if (GetMetadataPartitionState(*metadata, snapshot_name) == if (GetMetadataPartitionState(*metadata, snapshot_name) == MetadataPartitionState::Updated) { MetadataPartitionState::Updated) { LOG(WARNING) << "Cannot cancel update because snapshot" << snapshot_name all_snapshots_cancelled = false; << " is in use."; all_snapshot_cancelled = false; continue; continue; } } // Delete snapshots for partitions that are re-flashed after the update. // Delete snapshots for partitions that are re-flashed after the update. LOG(INFO) << "Detected re-flashing of partition " << snapshot_name << "."; LOG(WARNING) << "Detected re-flashing of partition " << snapshot_name << "."; if (!DeleteSnapshot(lock, snapshot_name)) { // This is an error, but it is okay to leave the snapshot in the short term. // However, if all_snapshot_cancelled == false after exiting the loop, caller may // initiate merge for this unused snapshot, which is likely to fail. LOG(WARNING) << "Failed to delete snapshot for re-flashed partition " << snapshot_name; } } if (all_snapshots_cancelled) { LOG(WARNING) << "All partitions are re-flashed after update, removing all update states."; } } if (!all_snapshot_cancelled) return false; return all_snapshots_cancelled; LOG(INFO) << "All partitions are re-flashed after update, removing all update states."; return true; } } bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) { bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) { Loading Loading @@ -1344,7 +1371,16 @@ bool SnapshotManager::NeedSnapshotsInFirstStageMount() { // the reason be clearer? Because the indicator file still exists, and // the reason be clearer? Because the indicator file still exists, and // if this was FATAL, reverting to the old slot would be broken. // if this was FATAL, reverting to the old slot would be broken. auto slot = GetCurrentSlot(); auto slot = GetCurrentSlot(); if (slot != Slot::Target) { if (slot != Slot::Target) { if (slot == Slot::Source && !device_->IsRecovery()) { // Device is rebooting into the original slot, so mark this as a // rollback. auto path = GetRollbackIndicatorPath(); if (!android::base::WriteStringToFile("1", path)) { PLOG(ERROR) << "Unable to write rollback indicator: " << path; } } LOG(INFO) << "Not booting from new slot. Will not mount snapshots."; LOG(INFO) << "Not booting from new slot. Will not mount snapshots."; return false; return false; } } Loading Loading @@ -2370,6 +2406,10 @@ UpdateState SnapshotManager::InitiateMergeAndWait() { return state; return state; } } if (state == UpdateState::Unverified) { if (state == UpdateState::Unverified) { if (GetCurrentSlot() != Slot::Target) { LOG(INFO) << "Cannot merge until device reboots."; return state; } if (!InitiateMerge()) { if (!InitiateMerge()) { LOG(ERROR) << "Failed to initiate merge."; LOG(ERROR) << "Failed to initiate merge."; return state; return state; Loading
fs_mgr/libsnapshot/snapshot_test.cpp +9 −1 Original line number Original line Diff line number Diff line Loading @@ -447,6 +447,9 @@ TEST_F(SnapshotTest, FirstStageMountAfterRollback) { auto sm = SnapshotManager::NewForFirstStageMount(info); auto sm = SnapshotManager::NewForFirstStageMount(info); ASSERT_NE(sm, nullptr); ASSERT_NE(sm, nullptr); ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount()); ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount()); auto indicator = sm->GetRollbackIndicatorPath(); ASSERT_EQ(access(indicator.c_str(), R_OK), 0); } } TEST_F(SnapshotTest, Merge) { TEST_F(SnapshotTest, Merge) { Loading Loading @@ -1015,6 +1018,9 @@ TEST_F(SnapshotUpdateTest, FullUpdateFlow) { ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount()); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_)); auto indicator = sm->GetRollbackIndicatorPath(); ASSERT_NE(access(indicator.c_str(), R_OK), 0); // Check that the target partitions have the same content. // Check that the target partitions have the same content. for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { ASSERT_TRUE(IsPartitionUnchanged(name)); ASSERT_TRUE(IsPartitionUnchanged(name)); Loading Loading @@ -1681,9 +1687,11 @@ TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) { ASSERT_NE(nullptr, flashed_builder->FindPartition("prd" + flashed_slot_suffix)); ASSERT_NE(nullptr, flashed_builder->FindPartition("prd" + flashed_slot_suffix)); flashed_builder->RemovePartition("prd" + flashed_slot_suffix); flashed_builder->RemovePartition("prd" + flashed_slot_suffix); // Note that fastbootd always updates the partition table of both slots. auto flashed_metadata = flashed_builder->Export(); auto flashed_metadata = flashed_builder->Export(); ASSERT_NE(nullptr, flashed_metadata); ASSERT_NE(nullptr, flashed_metadata); ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, flashed_slot)); ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, 0)); ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *flashed_metadata, 1)); std::string path; std::string path; for (const auto& name : {"sys", "vnd"}) { for (const auto& name : {"sys", "vnd"}) { Loading
fs_mgr/libsnapshot/snapshotctl.cpp +3 −1 Original line number Original line Diff line number Diff line Loading @@ -112,7 +112,9 @@ bool MergeCmdHandler(int argc, char** argv) { auto state = SnapshotManager::New()->InitiateMergeAndWait(); auto state = SnapshotManager::New()->InitiateMergeAndWait(); if (state == UpdateState::None) { // We could wind up in the Unverified state if the device rolled back or // hasn't fully rebooted. Ignore this. if (state == UpdateState::None || state == UpdateState::Unverified) { return true; return true; } } if (state == UpdateState::MergeCompleted) { if (state == UpdateState::MergeCompleted) { Loading