Merge changes from topic "snapshotctl_cleanup" into rvc-dev
* changes: libsnapshot: SnapshotMergeStats::Start/Finish libsnapshot: Expose SnapshotMergeStats libsnapshot: delete WaitForMerge. libsnapshot: handle errors in RemoveAllUpdateState appropriately. libsnapshot: remove snapshots properly after flashing libsnapshot: RemoveUpdateState on rollback. libsnapshot: NeedSnapshotsInFirstStageMount don't test for IsRecovery Allow ProcessUpdateState to be paused. libsnapshot: Re-expose InitiateMerge and ProcessUpdateState libsnapshot: Add prolog to RemoveAllUpdateStates. snapshotctl don't auto-merge. snapshotctl: init reports merge statistics libsnapshot/test: Re-enable the failing tests
This commit is contained in:
commit
133c060693
12 changed files with 264 additions and 197 deletions
|
@ -90,3 +90,4 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/product_services)
|
|||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/debug_ramdisk/product_services)
|
||||
$(call add-clean-step, find $(PRODUCT_OUT) -type l -name "charger" -print0 | xargs -0 rm -f)
|
||||
$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/bin/adbd)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/snapshotctl.rc)
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
"name": "fiemap_writer_test"
|
||||
},
|
||||
{
|
||||
"name": "vts_libsnapshot_test_presubmit"
|
||||
"name": "vts_libsnapshot_test"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -208,14 +208,6 @@ cc_test {
|
|||
defaults: ["libsnapshot_test_defaults"],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "vts_libsnapshot_test_presubmit",
|
||||
defaults: ["libsnapshot_test_defaults"],
|
||||
cppflags: [
|
||||
"-DSKIP_TEST_IN_PRESUBMIT",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "snapshotctl",
|
||||
srcs: [
|
||||
|
@ -243,7 +235,4 @@ cc_binary {
|
|||
// TODO(b/148818798): remove when parent bug is fixed.
|
||||
"libutilscallstack",
|
||||
],
|
||||
init_rc: [
|
||||
"snapshotctl.rc",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ class Return {
|
|||
enum class ErrorCode : int32_t {
|
||||
SUCCESS = static_cast<int32_t>(FiemapStatus::ErrorCode::SUCCESS),
|
||||
ERROR = static_cast<int32_t>(FiemapStatus::ErrorCode::ERROR),
|
||||
NEEDS_REBOOT = ERROR + 1,
|
||||
NO_SPACE = static_cast<int32_t>(FiemapStatus::ErrorCode::NO_SPACE),
|
||||
};
|
||||
ErrorCode error_code() const { return error_code_; }
|
||||
|
@ -43,7 +42,6 @@ class Return {
|
|||
static Return Ok() { return Return(ErrorCode::SUCCESS); }
|
||||
static Return Error() { return Return(ErrorCode::ERROR); }
|
||||
static Return NoSpace(uint64_t size) { return Return(ErrorCode::NO_SPACE, size); }
|
||||
static Return NeedsReboot() { return Return(ErrorCode::NEEDS_REBOOT); }
|
||||
// Does not set required_size_ properly even when status.error_code() == NO_SPACE.
|
||||
explicit Return(const FiemapStatus& status)
|
||||
: error_code_(FromFiemapStatusErrorCode(status.error_code())), required_size_(0) {}
|
||||
|
|
|
@ -140,7 +140,6 @@ class SnapshotManager final {
|
|||
// Before calling this function, all snapshots must be mapped.
|
||||
bool FinishedSnapshotWrites();
|
||||
|
||||
private:
|
||||
// Initiate a merge on all snapshot devices. This should only be used after an
|
||||
// update has been marked successful after booting.
|
||||
bool InitiateMerge();
|
||||
|
@ -149,7 +148,11 @@ class SnapshotManager final {
|
|||
// /data is mounted.
|
||||
//
|
||||
// If a merge is in progress, this function will block until the merge is
|
||||
// completed. If a merge or update was cancelled, this will clean up any
|
||||
// completed.
|
||||
// - Callback is called periodically during the merge. If callback()
|
||||
// returns false during the merge, ProcessUpdateState() will pause
|
||||
// and returns Merging.
|
||||
// If a merge or update was cancelled, this will clean up any
|
||||
// update artifacts and return.
|
||||
//
|
||||
// Note that after calling this, GetUpdateState() may still return that a
|
||||
|
@ -169,9 +172,9 @@ class SnapshotManager final {
|
|||
//
|
||||
// The optional callback allows the caller to periodically check the
|
||||
// progress with GetUpdateState().
|
||||
UpdateState ProcessUpdateState(const std::function<void()>& callback = {});
|
||||
UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
|
||||
const std::function<bool()>& before_cancel = {});
|
||||
|
||||
public:
|
||||
// Initiate the merge if necessary, then wait for the merge to finish.
|
||||
// See InitiateMerge() and ProcessUpdateState() for details.
|
||||
// Returns:
|
||||
|
@ -179,16 +182,8 @@ class SnapshotManager final {
|
|||
// - Unverified if called on the source slot
|
||||
// - MergeCompleted if merge is completed
|
||||
// - other states indicating an error has occurred
|
||||
UpdateState InitiateMergeAndWait(SnapshotMergeReport* report = nullptr);
|
||||
|
||||
// Wait for the merge if rebooted into the new slot. Does NOT initiate a
|
||||
// merge. If the merge has not been initiated (but should be), wait.
|
||||
// Returns:
|
||||
// - Return::Ok(): there is no merge or merge finishes
|
||||
// - Return::NeedsReboot(): merge finishes but need a reboot before
|
||||
// applying the next update.
|
||||
// - Return::Error(): other irrecoverable errors
|
||||
Return WaitForMerge();
|
||||
UpdateState InitiateMergeAndWait(SnapshotMergeReport* report = nullptr,
|
||||
const std::function<bool()>& before_cancel = {});
|
||||
|
||||
// Find the status of the current update, if any.
|
||||
//
|
||||
|
@ -375,14 +370,23 @@ class SnapshotManager final {
|
|||
|
||||
// Check for a cancelled or rolled back merge, returning true if such a
|
||||
// condition was detected and handled.
|
||||
bool HandleCancelledUpdate(LockedFile* lock);
|
||||
bool HandleCancelledUpdate(LockedFile* lock, const std::function<bool()>& before_cancel);
|
||||
|
||||
// Helper for HandleCancelledUpdate. Assumes booting from new slot.
|
||||
bool AreAllSnapshotsCancelled(LockedFile* lock);
|
||||
|
||||
// Determine whether partition names in |snapshots| have been flashed and
|
||||
// store result to |out|.
|
||||
// Return true if values are successfully retrieved and false on error
|
||||
// (e.g. super partition metadata cannot be read). When it returns true,
|
||||
// |out| stores true for partitions that have been flashed and false for
|
||||
// partitions that have not been flashed.
|
||||
bool GetSnapshotFlashingStatus(LockedFile* lock, const std::vector<std::string>& snapshots,
|
||||
std::map<std::string, bool>* out);
|
||||
|
||||
// Remove artifacts created by the update process, such as snapshots, and
|
||||
// set the update state to None.
|
||||
bool RemoveAllUpdateState(LockedFile* lock);
|
||||
bool RemoveAllUpdateState(LockedFile* lock, const std::function<bool()>& prolog = {});
|
||||
|
||||
// Interact with /metadata/ota.
|
||||
std::unique_ptr<LockedFile> OpenLock(int lock_flags);
|
||||
|
@ -437,8 +441,8 @@ class SnapshotManager final {
|
|||
// UpdateState::MergeCompleted
|
||||
// UpdateState::MergeFailed
|
||||
// UpdateState::MergeNeedsReboot
|
||||
UpdateState CheckMergeState();
|
||||
UpdateState CheckMergeState(LockedFile* lock);
|
||||
UpdateState CheckMergeState(const std::function<bool()>& before_cancel);
|
||||
UpdateState CheckMergeState(LockedFile* lock, const std::function<bool()>& before_cancel);
|
||||
UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name);
|
||||
|
||||
// Interact with status files under /metadata/ota/snapshots.
|
||||
|
@ -513,6 +517,11 @@ class SnapshotManager final {
|
|||
|
||||
std::string ReadUpdateSourceSlotSuffix();
|
||||
|
||||
// Helper for RemoveAllSnapshots.
|
||||
// Check whether |name| should be deleted as a snapshot name.
|
||||
bool ShouldDeleteSnapshot(LockedFile* lock, const std::map<std::string, bool>& flashing_status,
|
||||
Slot current_slot, const std::string& name);
|
||||
|
||||
std::string gsid_dir_;
|
||||
std::string metadata_dir_;
|
||||
std::unique_ptr<IDeviceInfo> device_;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#include <android/snapshot/snapshot.pb.h>
|
||||
#include <libsnapshot/snapshot.h>
|
||||
|
@ -24,21 +25,34 @@ namespace snapshot {
|
|||
|
||||
class SnapshotMergeStats {
|
||||
public:
|
||||
SnapshotMergeStats(SnapshotManager& parent);
|
||||
~SnapshotMergeStats();
|
||||
void Start();
|
||||
void Resume();
|
||||
// Not thread safe.
|
||||
static SnapshotMergeStats* GetInstance(SnapshotManager& manager);
|
||||
|
||||
// Called when merge starts or resumes.
|
||||
bool Start();
|
||||
void set_state(android::snapshot::UpdateState state);
|
||||
SnapshotMergeReport GetReport();
|
||||
|
||||
// Called when merge ends. Properly clean up permanent storage.
|
||||
class Result {
|
||||
public:
|
||||
virtual ~Result() {}
|
||||
virtual const SnapshotMergeReport& report() const = 0;
|
||||
// Time between successful Start() / Resume() to Finish().
|
||||
virtual std::chrono::steady_clock::duration merge_time() const = 0;
|
||||
};
|
||||
std::unique_ptr<Result> Finish();
|
||||
|
||||
private:
|
||||
bool ReadState();
|
||||
bool WriteState();
|
||||
bool DeleteState();
|
||||
SnapshotMergeStats(const std::string& path);
|
||||
|
||||
const SnapshotManager& parent_;
|
||||
std::string path_;
|
||||
SnapshotMergeReport report_;
|
||||
std::chrono::time_point<std::chrono::steady_clock> init_time_;
|
||||
std::chrono::time_point<std::chrono::steady_clock> end_time_;
|
||||
// Time of the last successful Start() / Resume() call.
|
||||
std::chrono::time_point<std::chrono::steady_clock> start_time_;
|
||||
bool running_{false};
|
||||
};
|
||||
|
||||
} // namespace snapshot
|
|
@ -24,8 +24,6 @@ std::string Return::string() const {
|
|||
switch (error_code()) {
|
||||
case ErrorCode::ERROR:
|
||||
return "Error";
|
||||
case ErrorCode::NEEDS_REBOOT:
|
||||
return "Retry after reboot";
|
||||
case ErrorCode::SUCCESS:
|
||||
[[fallthrough]];
|
||||
case ErrorCode::NO_SPACE:
|
||||
|
|
|
@ -43,10 +43,10 @@
|
|||
#endif
|
||||
|
||||
#include <android/snapshot/snapshot.pb.h>
|
||||
#include <libsnapshot/snapshot_stats.h>
|
||||
#include "device_info.h"
|
||||
#include "partition_cow_creator.h"
|
||||
#include "snapshot_metadata_updater.h"
|
||||
#include "snapshot_stats.h"
|
||||
#include "utility.h"
|
||||
|
||||
namespace android {
|
||||
|
@ -219,7 +219,12 @@ static bool RemoveFileIfExists(const std::string& path) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) {
|
||||
bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock, const std::function<bool()>& prolog) {
|
||||
if (prolog && !prolog()) {
|
||||
LOG(WARNING) << "Can't RemoveAllUpdateState: prolog failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Removing all update state.";
|
||||
|
||||
#ifdef LIBSNAPSHOT_USE_CALLSTACK
|
||||
|
@ -789,9 +794,10 @@ bool SnapshotManager::QuerySnapshotStatus(const std::string& dm_name, std::strin
|
|||
// Note that when a merge fails, we will *always* try again to complete the
|
||||
// merge each time the device boots. There is no harm in doing so, and if
|
||||
// the problem was transient, we might manage to get a new outcome.
|
||||
UpdateState SnapshotManager::ProcessUpdateState(const std::function<void()>& callback) {
|
||||
UpdateState SnapshotManager::ProcessUpdateState(const std::function<bool()>& callback,
|
||||
const std::function<bool()>& before_cancel) {
|
||||
while (true) {
|
||||
UpdateState state = CheckMergeState();
|
||||
UpdateState state = CheckMergeState(before_cancel);
|
||||
if (state == UpdateState::MergeFailed) {
|
||||
AcknowledgeMergeFailure();
|
||||
}
|
||||
|
@ -801,8 +807,8 @@ UpdateState SnapshotManager::ProcessUpdateState(const std::function<void()>& cal
|
|||
return state;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback();
|
||||
if (callback && !callback()) {
|
||||
return state;
|
||||
}
|
||||
|
||||
// This wait is not super time sensitive, so we have a relatively
|
||||
|
@ -811,24 +817,27 @@ UpdateState SnapshotManager::ProcessUpdateState(const std::function<void()>& cal
|
|||
}
|
||||
}
|
||||
|
||||
UpdateState SnapshotManager::CheckMergeState() {
|
||||
UpdateState SnapshotManager::CheckMergeState(const std::function<bool()>& before_cancel) {
|
||||
auto lock = LockExclusive();
|
||||
if (!lock) {
|
||||
return UpdateState::MergeFailed;
|
||||
}
|
||||
|
||||
UpdateState state = CheckMergeState(lock.get());
|
||||
UpdateState state = CheckMergeState(lock.get(), before_cancel);
|
||||
if (state == UpdateState::MergeCompleted) {
|
||||
// Do this inside the same lock. Failures get acknowledged without the
|
||||
// lock, because flock() might have failed.
|
||||
AcknowledgeMergeSuccess(lock.get());
|
||||
} else if (state == UpdateState::Cancelled) {
|
||||
RemoveAllUpdateState(lock.get());
|
||||
if (!RemoveAllUpdateState(lock.get(), before_cancel)) {
|
||||
return ReadSnapshotUpdateStatus(lock.get()).state();
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
UpdateState SnapshotManager::CheckMergeState(LockedFile* lock) {
|
||||
UpdateState SnapshotManager::CheckMergeState(LockedFile* lock,
|
||||
const std::function<bool()>& before_cancel) {
|
||||
UpdateState state = ReadUpdateState(lock);
|
||||
switch (state) {
|
||||
case UpdateState::None:
|
||||
|
@ -849,7 +858,7 @@ UpdateState SnapshotManager::CheckMergeState(LockedFile* lock) {
|
|||
// This is an edge case. Normally cancelled updates are detected
|
||||
// via the merge poll below, but if we never started a merge, we
|
||||
// need to also check here.
|
||||
if (HandleCancelledUpdate(lock)) {
|
||||
if (HandleCancelledUpdate(lock, before_cancel)) {
|
||||
return UpdateState::Cancelled;
|
||||
}
|
||||
return state;
|
||||
|
@ -1169,7 +1178,8 @@ bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock) {
|
||||
bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock,
|
||||
const std::function<bool()>& before_cancel) {
|
||||
auto slot = GetCurrentSlot();
|
||||
if (slot == Slot::Unknown) {
|
||||
return false;
|
||||
|
@ -1177,15 +1187,30 @@ bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock) {
|
|||
|
||||
// If all snapshots were reflashed, then cancel the entire update.
|
||||
if (AreAllSnapshotsCancelled(lock)) {
|
||||
RemoveAllUpdateState(lock);
|
||||
return true;
|
||||
LOG(WARNING) << "Detected re-flashing, cancelling unverified update.";
|
||||
return RemoveAllUpdateState(lock, before_cancel);
|
||||
}
|
||||
|
||||
// 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;
|
||||
// If update has been rolled back, then cancel the entire update.
|
||||
// Client (update_engine) is responsible for doing additional cleanup work on its own states
|
||||
// when ProcessUpdateState() returns UpdateState::Cancelled.
|
||||
auto current_slot = GetCurrentSlot();
|
||||
if (current_slot != Slot::Source) {
|
||||
LOG(INFO) << "Update state is being processed while booting at " << current_slot
|
||||
<< " slot, taking no action.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// current_slot == Source. Attempt to detect rollbacks.
|
||||
if (access(GetRollbackIndicatorPath().c_str(), F_OK) != 0) {
|
||||
// This unverified update is not attempted. Take no action.
|
||||
PLOG(INFO) << "Rollback indicator not detected. "
|
||||
<< "Update state is being processed before reboot, taking no action.";
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(WARNING) << "Detected rollback, cancelling unverified update.";
|
||||
return RemoveAllUpdateState(lock, before_cancel);
|
||||
}
|
||||
|
||||
std::unique_ptr<LpMetadata> SnapshotManager::ReadCurrentMetadata() {
|
||||
|
@ -1219,6 +1244,28 @@ bool SnapshotManager::AreAllSnapshotsCancelled(LockedFile* lock) {
|
|||
return true;
|
||||
}
|
||||
|
||||
std::map<std::string, bool> flashing_status;
|
||||
|
||||
if (!GetSnapshotFlashingStatus(lock, snapshots, &flashing_status)) {
|
||||
LOG(WARNING) << "Failed to determine whether partitions have been flashed. Not"
|
||||
<< "removing update states.";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool all_snapshots_cancelled = std::all_of(flashing_status.begin(), flashing_status.end(),
|
||||
[](const auto& pair) { return pair.second; });
|
||||
|
||||
if (all_snapshots_cancelled) {
|
||||
LOG(WARNING) << "All partitions are re-flashed after update, removing all update states.";
|
||||
}
|
||||
return all_snapshots_cancelled;
|
||||
}
|
||||
|
||||
bool SnapshotManager::GetSnapshotFlashingStatus(LockedFile* lock,
|
||||
const std::vector<std::string>& snapshots,
|
||||
std::map<std::string, bool>* out) {
|
||||
CHECK(lock);
|
||||
|
||||
auto source_slot_suffix = ReadUpdateSourceSlotSuffix();
|
||||
if (source_slot_suffix.empty()) {
|
||||
return false;
|
||||
|
@ -1244,20 +1291,17 @@ bool SnapshotManager::AreAllSnapshotsCancelled(LockedFile* lock) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool all_snapshots_cancelled = true;
|
||||
for (const auto& snapshot_name : snapshots) {
|
||||
if (GetMetadataPartitionState(*metadata, snapshot_name) ==
|
||||
MetadataPartitionState::Updated) {
|
||||
all_snapshots_cancelled = false;
|
||||
continue;
|
||||
out->emplace(snapshot_name, false);
|
||||
} else {
|
||||
// Delete snapshots for partitions that are re-flashed after the update.
|
||||
LOG(WARNING) << "Detected re-flashing of partition " << snapshot_name << ".";
|
||||
out->emplace(snapshot_name, true);
|
||||
}
|
||||
// Delete snapshots for partitions that are re-flashed after the update.
|
||||
LOG(WARNING) << "Detected re-flashing of partition " << snapshot_name << ".";
|
||||
}
|
||||
if (all_snapshots_cancelled) {
|
||||
LOG(WARNING) << "All partitions are re-flashed after update, removing all update states.";
|
||||
}
|
||||
return all_snapshots_cancelled;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
|
||||
|
@ -1267,10 +1311,38 @@ bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
|
|||
return false;
|
||||
}
|
||||
|
||||
std::map<std::string, bool> flashing_status;
|
||||
if (!GetSnapshotFlashingStatus(lock, snapshots, &flashing_status)) {
|
||||
LOG(WARNING) << "Failed to get flashing status";
|
||||
}
|
||||
|
||||
auto current_slot = GetCurrentSlot();
|
||||
bool ok = true;
|
||||
bool has_mapped_cow_images = false;
|
||||
for (const auto& name : snapshots) {
|
||||
if (!UnmapPartitionWithSnapshot(lock, name) || !DeleteSnapshot(lock, name)) {
|
||||
// If booting off source slot, it is okay to unmap and delete all the snapshots.
|
||||
// If boot indicator is missing, update state is None or Initiated, so
|
||||
// it is also okay to unmap and delete all the snapshots.
|
||||
// If booting off target slot,
|
||||
// - should not unmap because:
|
||||
// - In Android mode, snapshots are not mapped, but
|
||||
// filesystems are mounting off dm-linear targets directly.
|
||||
// - In recovery mode, assume nothing is mapped, so it is optional to unmap.
|
||||
// - If partition is flashed or unknown, it is okay to delete snapshots.
|
||||
// Otherwise (UPDATED flag), only delete snapshots if they are not mapped
|
||||
// as dm-snapshot (for example, after merge completes).
|
||||
bool should_unmap = current_slot != Slot::Target;
|
||||
bool should_delete = ShouldDeleteSnapshot(lock, flashing_status, current_slot, name);
|
||||
|
||||
bool partition_ok = true;
|
||||
if (should_unmap && !UnmapPartitionWithSnapshot(lock, name)) {
|
||||
partition_ok = false;
|
||||
}
|
||||
if (partition_ok && should_delete && !DeleteSnapshot(lock, name)) {
|
||||
partition_ok = false;
|
||||
}
|
||||
|
||||
if (!partition_ok) {
|
||||
// Remember whether or not we were able to unmap the cow image.
|
||||
auto cow_image_device = GetCowImageDeviceName(name);
|
||||
has_mapped_cow_images |=
|
||||
|
@ -1293,6 +1365,34 @@ bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
|
|||
return ok;
|
||||
}
|
||||
|
||||
// See comments in RemoveAllSnapshots().
|
||||
bool SnapshotManager::ShouldDeleteSnapshot(LockedFile* lock,
|
||||
const std::map<std::string, bool>& flashing_status,
|
||||
Slot current_slot, const std::string& name) {
|
||||
if (current_slot != Slot::Target) {
|
||||
return true;
|
||||
}
|
||||
auto it = flashing_status.find(name);
|
||||
if (it == flashing_status.end()) {
|
||||
LOG(WARNING) << "Can't determine flashing status for " << name;
|
||||
return true;
|
||||
}
|
||||
if (it->second) {
|
||||
// partition flashed, okay to delete obsolete snapshots
|
||||
return true;
|
||||
}
|
||||
// partition updated, only delete if not dm-snapshot
|
||||
SnapshotStatus status;
|
||||
if (!ReadSnapshotStatus(lock, name, &status)) {
|
||||
LOG(WARNING) << "Unable to read snapshot status for " << name
|
||||
<< ", guessing snapshot device name";
|
||||
auto extra_name = GetSnapshotExtraDeviceName(name);
|
||||
return !IsSnapshotDevice(name) && !IsSnapshotDevice(extra_name);
|
||||
}
|
||||
auto dm_name = GetSnapshotDeviceName(name, status);
|
||||
return !IsSnapshotDevice(dm_name);
|
||||
}
|
||||
|
||||
UpdateState SnapshotManager::GetUpdateState(double* progress) {
|
||||
// If we've never started an update, the state file won't exist.
|
||||
auto state_file = GetStateFilePath();
|
||||
|
@ -1379,12 +1479,14 @@ bool SnapshotManager::NeedSnapshotsInFirstStageMount() {
|
|||
auto slot = GetCurrentSlot();
|
||||
|
||||
if (slot != Slot::Target) {
|
||||
if (slot == Slot::Source && !device_->IsRecovery()) {
|
||||
if (slot == Slot::Source) {
|
||||
// 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;
|
||||
} else {
|
||||
LOG(INFO) << "Rollback detected, writing rollback indicator to " << path;
|
||||
}
|
||||
}
|
||||
LOG(INFO) << "Not booting from new slot. Will not mount snapshots.";
|
||||
|
@ -2388,7 +2490,8 @@ std::unique_ptr<AutoDevice> SnapshotManager::EnsureMetadataMounted() {
|
|||
return AutoUnmountDevice::New(device_->GetMetadataDir());
|
||||
}
|
||||
|
||||
UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_report) {
|
||||
UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_report,
|
||||
const std::function<bool()>& before_cancel) {
|
||||
{
|
||||
auto lock = LockExclusive();
|
||||
// Sync update state from file with bootloader.
|
||||
|
@ -2398,23 +2501,24 @@ UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_rep
|
|||
}
|
||||
}
|
||||
|
||||
SnapshotMergeStats merge_stats(*this);
|
||||
auto merge_stats = SnapshotMergeStats::GetInstance(*this);
|
||||
|
||||
unsigned int last_progress = 0;
|
||||
auto callback = [&]() -> void {
|
||||
auto callback = [&]() -> bool {
|
||||
double progress;
|
||||
GetUpdateState(&progress);
|
||||
if (last_progress < static_cast<unsigned int>(progress)) {
|
||||
last_progress = progress;
|
||||
LOG(INFO) << "Waiting for merge to complete: " << last_progress << "%.";
|
||||
}
|
||||
return true; // continue
|
||||
};
|
||||
|
||||
LOG(INFO) << "Waiting for any previous merge request to complete. "
|
||||
<< "This can take up to several minutes.";
|
||||
merge_stats.Resume();
|
||||
auto state = ProcessUpdateState(callback);
|
||||
merge_stats.set_state(state);
|
||||
merge_stats->Start();
|
||||
auto state = ProcessUpdateState(callback, before_cancel);
|
||||
merge_stats->set_state(state);
|
||||
if (state == UpdateState::None) {
|
||||
LOG(INFO) << "Can't find any snapshot to merge.";
|
||||
return state;
|
||||
|
@ -2425,10 +2529,6 @@ UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_rep
|
|||
return state;
|
||||
}
|
||||
|
||||
// This is the first snapshot merge that is requested after OTA. We can
|
||||
// initialize the merge duration statistics.
|
||||
merge_stats.Start();
|
||||
|
||||
if (!InitiateMerge()) {
|
||||
LOG(ERROR) << "Failed to initiate merge.";
|
||||
return state;
|
||||
|
@ -2436,43 +2536,22 @@ UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_rep
|
|||
// All other states can be handled by ProcessUpdateState.
|
||||
LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes.";
|
||||
last_progress = 0;
|
||||
state = ProcessUpdateState(callback);
|
||||
merge_stats.set_state(state);
|
||||
state = ProcessUpdateState(callback, before_cancel);
|
||||
merge_stats->set_state(state);
|
||||
}
|
||||
|
||||
LOG(INFO) << "Merge finished with state \"" << state << "\".";
|
||||
if (stats_report) {
|
||||
*stats_report = merge_stats.GetReport();
|
||||
auto result = merge_stats->Finish();
|
||||
if (result) {
|
||||
*stats_report = result->report();
|
||||
} else {
|
||||
LOG(WARNING) << "SnapshotMergeStatus::Finish failed.";
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
Return SnapshotManager::WaitForMerge() {
|
||||
LOG(INFO) << "Waiting for any previous merge request to complete. "
|
||||
<< "This can take up to several minutes.";
|
||||
while (true) {
|
||||
auto state = ProcessUpdateState();
|
||||
if (state == UpdateState::Unverified && GetCurrentSlot() == Slot::Target) {
|
||||
LOG(INFO) << "Wait for merge to be initiated.";
|
||||
std::this_thread::sleep_for(kUpdateStateCheckInterval);
|
||||
continue;
|
||||
}
|
||||
LOG(INFO) << "Wait for merge exits with state " << state;
|
||||
switch (state) {
|
||||
case UpdateState::None:
|
||||
[[fallthrough]];
|
||||
case UpdateState::MergeCompleted:
|
||||
[[fallthrough]];
|
||||
case UpdateState::Cancelled:
|
||||
return Return::Ok();
|
||||
case UpdateState::MergeNeedsReboot:
|
||||
return Return::NeedsReboot();
|
||||
default:
|
||||
return Return::Error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callback) {
|
||||
if (!device_->IsRecovery()) {
|
||||
LOG(ERROR) << "Data wipes are only allowed in recovery.";
|
||||
|
@ -2503,7 +2582,10 @@ bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callba
|
|||
return false;
|
||||
}
|
||||
|
||||
UpdateState state = ProcessUpdateState(callback);
|
||||
UpdateState state = ProcessUpdateState([&]() -> bool {
|
||||
callback();
|
||||
return true;
|
||||
});
|
||||
LOG(INFO) << "Update state in recovery: " << state;
|
||||
switch (state) {
|
||||
case UpdateState::MergeFailed:
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "snapshot_stats.h"
|
||||
#include <libsnapshot/snapshot_stats.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
|
@ -23,22 +23,17 @@
|
|||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
SnapshotMergeStats::SnapshotMergeStats(SnapshotManager& parent) : parent_(parent) {
|
||||
init_time_ = std::chrono::steady_clock::now();
|
||||
SnapshotMergeStats* SnapshotMergeStats::GetInstance(SnapshotManager& parent) {
|
||||
static SnapshotMergeStats g_instance(parent.GetMergeStateFilePath());
|
||||
CHECK(g_instance.path_ == parent.GetMergeStateFilePath());
|
||||
return &g_instance;
|
||||
}
|
||||
|
||||
SnapshotMergeStats::~SnapshotMergeStats() {
|
||||
std::string error;
|
||||
auto file_path = parent_.GetMergeStateFilePath();
|
||||
if (!android::base::RemoveFileIfExists(file_path, &error)) {
|
||||
LOG(ERROR) << "Failed to remove merge statistics file " << file_path << ": " << error;
|
||||
return;
|
||||
}
|
||||
}
|
||||
SnapshotMergeStats::SnapshotMergeStats(const std::string& path) : path_(path), running_(false) {}
|
||||
|
||||
bool SnapshotMergeStats::ReadState() {
|
||||
std::string contents;
|
||||
if (!android::base::ReadFileToString(parent_.GetMergeStateFilePath(), &contents)) {
|
||||
if (!android::base::ReadFileToString(path_, &contents)) {
|
||||
PLOG(INFO) << "Read merge statistics file failed";
|
||||
return false;
|
||||
}
|
||||
|
@ -55,34 +50,73 @@ bool SnapshotMergeStats::WriteState() {
|
|||
LOG(ERROR) << "Unable to serialize SnapshotMergeStats.";
|
||||
return false;
|
||||
}
|
||||
auto file_path = parent_.GetMergeStateFilePath();
|
||||
if (!WriteStringToFileAtomic(contents, file_path)) {
|
||||
if (!WriteStringToFileAtomic(contents, path_)) {
|
||||
PLOG(ERROR) << "Could not write to merge statistics file";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SnapshotMergeStats::Start() {
|
||||
report_.set_resume_count(0);
|
||||
report_.set_state(UpdateState::None);
|
||||
WriteState();
|
||||
bool SnapshotMergeStats::DeleteState() {
|
||||
std::string error;
|
||||
if (!android::base::RemoveFileIfExists(path_, &error)) {
|
||||
LOG(ERROR) << "Failed to remove merge statistics file " << path_ << ": " << error;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SnapshotMergeStats::Resume() {
|
||||
if (!ReadState()) {
|
||||
return;
|
||||
bool SnapshotMergeStats::Start() {
|
||||
if (running_) {
|
||||
LOG(ERROR) << "SnapshotMergeStats running_ == " << running_;
|
||||
return false;
|
||||
}
|
||||
report_.set_resume_count(report_.resume_count() + 1);
|
||||
WriteState();
|
||||
running_ = true;
|
||||
|
||||
start_time_ = std::chrono::steady_clock::now();
|
||||
if (ReadState()) {
|
||||
report_.set_resume_count(report_.resume_count() + 1);
|
||||
} else {
|
||||
report_.set_resume_count(0);
|
||||
report_.set_state(UpdateState::None);
|
||||
}
|
||||
|
||||
return WriteState();
|
||||
}
|
||||
|
||||
void SnapshotMergeStats::set_state(android::snapshot::UpdateState state) {
|
||||
report_.set_state(state);
|
||||
}
|
||||
|
||||
SnapshotMergeReport SnapshotMergeStats::GetReport() {
|
||||
return report_;
|
||||
class SnapshotMergeStatsResultImpl : public SnapshotMergeStats::Result {
|
||||
public:
|
||||
SnapshotMergeStatsResultImpl(const SnapshotMergeReport& report,
|
||||
std::chrono::steady_clock::duration merge_time)
|
||||
: report_(report), merge_time_(merge_time) {}
|
||||
const SnapshotMergeReport& report() const override { return report_; }
|
||||
std::chrono::steady_clock::duration merge_time() const override { return merge_time_; }
|
||||
|
||||
private:
|
||||
SnapshotMergeReport report_;
|
||||
std::chrono::steady_clock::duration merge_time_;
|
||||
};
|
||||
|
||||
std::unique_ptr<SnapshotMergeStats::Result> SnapshotMergeStats::Finish() {
|
||||
if (!running_) {
|
||||
LOG(ERROR) << "SnapshotMergeStats running_ == " << running_;
|
||||
return nullptr;
|
||||
}
|
||||
running_ = false;
|
||||
|
||||
auto result = std::make_unique<SnapshotMergeStatsResultImpl>(
|
||||
report_, std::chrono::steady_clock::now() - start_time_);
|
||||
|
||||
// We still want to report result if state is not deleted. Just leave
|
||||
// it there and move on. A side effect is that it may be reported over and
|
||||
// over again in the future, but there is nothing we can do.
|
||||
(void)DeleteState();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
|
|
|
@ -506,9 +506,6 @@ TEST_F(SnapshotTest, Merge) {
|
|||
}
|
||||
|
||||
TEST_F(SnapshotTest, FirstStageMountAndMerge) {
|
||||
#ifdef SKIP_TEST_IN_PRESUBMIT
|
||||
GTEST_SKIP() << "WIP failure b/148889015";
|
||||
#endif
|
||||
ASSERT_TRUE(AcquireLock());
|
||||
|
||||
static const uint64_t kDeviceSize = 1024 * 1024;
|
||||
|
@ -565,9 +562,6 @@ TEST_F(SnapshotTest, FlashSuperDuringUpdate) {
|
|||
}
|
||||
|
||||
TEST_F(SnapshotTest, FlashSuperDuringMerge) {
|
||||
#ifdef SKIP_TEST_IN_PRESUBMIT
|
||||
GTEST_SKIP() << "WIP failure b/148889015";
|
||||
#endif
|
||||
ASSERT_TRUE(AcquireLock());
|
||||
|
||||
static const uint64_t kDeviceSize = 1024 * 1024;
|
||||
|
@ -979,9 +973,6 @@ class SnapshotUpdateTest : public SnapshotTest {
|
|||
// Also test UnmapUpdateSnapshot unmaps everything.
|
||||
// Also test first stage mount and merge after this.
|
||||
TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
|
||||
#ifdef SKIP_TEST_IN_PRESUBMIT
|
||||
GTEST_SKIP() << "WIP failure b/148889015";
|
||||
#endif
|
||||
// OTA client blindly unmaps all partitions that are possibly mapped.
|
||||
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
|
||||
ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
|
||||
|
@ -1129,9 +1120,6 @@ TEST_F(SnapshotUpdateTest, SnapshotStatusFileWithoutCow) {
|
|||
|
||||
// Test that the old partitions are not modified.
|
||||
TEST_F(SnapshotUpdateTest, TestRollback) {
|
||||
#ifdef SKIP_TEST_IN_PRESUBMIT
|
||||
GTEST_SKIP() << "WIP failure b/148889015";
|
||||
#endif
|
||||
// Execute the update.
|
||||
ASSERT_TRUE(sm->BeginUpdate());
|
||||
ASSERT_TRUE(sm->UnmapUpdateSnapshot("sys_b"));
|
||||
|
@ -1309,9 +1297,6 @@ TEST_F(SnapshotUpdateTest, RetrofitAfterRegularAb) {
|
|||
}
|
||||
|
||||
TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {
|
||||
#ifdef SKIP_TEST_IN_PRESUBMIT
|
||||
GTEST_SKIP() << "WIP failure b/148889015";
|
||||
#endif
|
||||
// Make source partitions as big as possible to force COW image to be created.
|
||||
SetSize(sys_, 5_MiB);
|
||||
SetSize(vnd_, 5_MiB);
|
||||
|
@ -1585,48 +1570,6 @@ TEST_F(SnapshotUpdateTest, Overflow) {
|
|||
<< "FinishedSnapshotWrites should detect overflow of CoW device.";
|
||||
}
|
||||
|
||||
TEST_F(SnapshotUpdateTest, WaitForMerge) {
|
||||
#ifdef SKIP_TEST_IN_PRESUBMIT
|
||||
GTEST_SKIP() << "WIP failure b/148889015";
|
||||
#endif
|
||||
AddOperationForPartitions();
|
||||
|
||||
// Execute the update.
|
||||
ASSERT_TRUE(sm->BeginUpdate());
|
||||
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
|
||||
|
||||
// Write some data to target partitions.
|
||||
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
|
||||
ASSERT_TRUE(WriteSnapshotAndHash(name));
|
||||
}
|
||||
|
||||
ASSERT_TRUE(sm->FinishedSnapshotWrites());
|
||||
|
||||
// Simulate shutting down the device.
|
||||
ASSERT_TRUE(UnmapAll());
|
||||
|
||||
// After reboot, init does first stage mount.
|
||||
{
|
||||
auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
|
||||
ASSERT_NE(nullptr, init);
|
||||
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
|
||||
}
|
||||
|
||||
auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
|
||||
ASSERT_NE(nullptr, new_sm);
|
||||
|
||||
auto waiter = std::async(std::launch::async, [&new_sm] { return new_sm->WaitForMerge(); });
|
||||
ASSERT_EQ(std::future_status::timeout, waiter.wait_for(1s))
|
||||
<< "WaitForMerge should block when not initiated";
|
||||
|
||||
auto merger =
|
||||
std::async(std::launch::async, [&new_sm] { return new_sm->InitiateMergeAndWait(); });
|
||||
// Small images, so should be merged pretty quickly.
|
||||
ASSERT_EQ(std::future_status::ready, waiter.wait_for(3s)) << "WaitForMerge did not finish";
|
||||
ASSERT_TRUE(waiter.get());
|
||||
ASSERT_THAT(merger.get(), AnyOf(UpdateState::None, UpdateState::MergeCompleted));
|
||||
}
|
||||
|
||||
TEST_F(SnapshotUpdateTest, LowSpace) {
|
||||
static constexpr auto kMaxFree = 10_MiB;
|
||||
auto userdata = std::make_unique<LowSpaceUserdata>();
|
||||
|
|
|
@ -26,9 +26,9 @@
|
|||
#include <android-base/unique_fd.h>
|
||||
#include <android/snapshot/snapshot.pb.h>
|
||||
#include <libsnapshot/snapshot.h>
|
||||
#include <libsnapshot/snapshot_stats.h>
|
||||
#include <statslog.h>
|
||||
|
||||
#include "snapshot_stats.h"
|
||||
#include "utility.h"
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
@ -178,6 +178,7 @@ bool MergeCmdHandler(int argc, char** argv) {
|
|||
}
|
||||
|
||||
LOG(ERROR) << "Snapshot failed to merge with state \"" << state << "\".";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
on property:sys.boot_completed=1
|
||||
exec_background - root root -- /system/bin/snapshotctl merge --logcat --log-to-file
|
Loading…
Reference in a new issue