diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h index 4fa4330b7..6ed55af2d 100644 --- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h +++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h @@ -85,6 +85,9 @@ class SnapuserdClient { // Returns Merge completion percentage double GetMergePercent(); + + // Return the status of the snapshot + std::string QuerySnapshotStatus(const std::string& misc_name); }; } // namespace snapshot diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp index 87c0ce493..e345269ae 100644 --- a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp +++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp @@ -252,5 +252,14 @@ double SnapuserdClient::GetMergePercent() { return std::stod(response); } +std::string SnapuserdClient::QuerySnapshotStatus(const std::string& misc_name) { + std::string msg = "getstatus," + misc_name; + if (!Sendmsg(msg)) { + LOG(ERROR) << "Failed to send message " << msg << " to snapuserd"; + return "snapshot-merge-failed"; + } + return Receivemsg(); +} + } // namespace snapshot } // namespace android diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h index 5e9c7bf14..13b56facb 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h @@ -268,6 +268,8 @@ class SnapshotHandler : public std::enable_shared_from_this { bool ShouldReconstructDataFromCow() { return populate_data_from_cow_; } void FinishReconstructDataFromCow() { populate_data_from_cow_ = false; } + // Return the snapshot status + std::string GetMergeStatus(); // RA related functions uint64_t GetBufferMetadataOffset(); diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp index a9b1d17ab..a4fd5a035 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp @@ -54,6 +54,7 @@ DaemonOps SnapuserServer::Resolveop(std::string& input) { if (input == "supports") return DaemonOps::SUPPORTS; if (input == "initiate_merge") return DaemonOps::INITIATE; if (input == "merge_percent") return DaemonOps::PERCENTAGE; + if (input == "getstatus") return DaemonOps::GETSTATUS; return DaemonOps::INVALID; } @@ -262,6 +263,25 @@ bool SnapuserServer::Receivemsg(android::base::borrowed_fd fd, const std::string return Sendmsg(fd, std::to_string(percentage)); } + case DaemonOps::GETSTATUS: { + // Message format: + // getstatus, + if (out.size() != 2) { + LOG(ERROR) << "Malformed delete message, " << out.size() << " parts"; + return Sendmsg(fd, "snapshot-merge-failed"); + } + { + std::lock_guard lock(lock_); + auto iter = FindHandler(&lock, out[1]); + if (iter == dm_users_.end()) { + LOG(ERROR) << "Could not find handler: " << out[1]; + return Sendmsg(fd, "snapshot-merge-failed"); + } + + std::string merge_status = GetMergeStatus(*iter); + return Sendmsg(fd, merge_status); + } + } default: { LOG(ERROR) << "Received unknown message type from client"; Sendmsg(fd, "fail"); @@ -513,6 +533,10 @@ void SnapuserServer::TerminateMergeThreads(std::lock_guard* proof_of } } +std::string SnapuserServer::GetMergeStatus(const std::shared_ptr& handler) { + return handler->snapuserd()->GetMergeStatus(); +} + double SnapuserServer::GetMergePercentage(std::lock_guard* proof_of_lock) { CHECK(proof_of_lock); double percentage = 0.0; diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h index 6fc3a9d97..e93621ca2 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h @@ -45,6 +45,7 @@ enum class DaemonOps { SUPPORTS, INITIATE, PERCENTAGE, + GETSTATUS, INVALID, }; @@ -131,6 +132,7 @@ class SnapuserServer { const std::string& base_path_merge); bool StartHandler(const std::shared_ptr& handler); bool StartMerge(const std::shared_ptr& handler); + std::string GetMergeStatus(const std::shared_ptr& handler); void SetTerminating() { terminating_ = true; } void ReceivedSocketSignal() { received_socket_signal_ = true; } diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp index ef7a1181f..6c91fde6b 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp @@ -359,6 +359,61 @@ void SnapshotHandler::WaitForMergeComplete() { } } +std::string SnapshotHandler::GetMergeStatus() { + bool merge_not_initiated = false; + bool merge_failed = false; + + { + std::lock_guard lock(lock_); + if (!MergeInitiated()) { + merge_not_initiated = true; + } + + if (io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED) { + merge_failed = true; + } + } + + struct CowHeader* ch = reinterpret_cast(mapped_addr_); + bool merge_complete = (ch->num_merge_ops == reader_->get_num_total_data_ops()); + + if (merge_not_initiated) { + // Merge was not initiated yet; however, we have merge completion + // recorded in the COW Header. This can happen if the device was + // rebooted during merge. During next reboot, libsnapshot will + // query the status and if the merge is completed, then snapshot-status + // file will be deleted + if (merge_complete) { + return "snapshot-merge-complete"; + } + + // Return the state as "snapshot". If the device was rebooted during + // merge, we will return the status as "snapshot". This is ok, as + // libsnapshot will explicitly resume the merge. This is slightly + // different from kernel snapshot wherein once the snapshot was switched + // to merge target, during next boot, we immediately switch to merge + // target. We don't do that here because, during first stage init, we + // don't want to initiate the merge. The problem is that we have daemon + // transition between first and second stage init. If the merge was + // started, then we will have to quiesce the merge before switching + // the dm tables. Instead, we just wait until second stage daemon is up + // before resuming the merge. + return "snapshot"; + } + + if (merge_failed) { + return "snapshot-merge-failed"; + } + + // Merge complete + if (merge_complete) { + return "snapshot-merge-complete"; + } + + // Merge is in-progress + return "snapshot-merge"; +} + //========== End of Read-ahead state transition functions ==================== /*