Move load & restore logs to logging.cpp

We perform these steps to perserve the recovery logs when wiping
/cache partition. Move them to logging.cpp to keep the actually
EraseVolume function concise.

Bug: 130166585
Test: unit tests pass, mount cache and check last log after cache
Change-Id: Idc52833817a446f3a0148a3dd2112f911c9ef48d
diff --git a/install/include/install/wipe_data.h b/install/include/install/wipe_data.h
index 06b1b95..b34891f 100644
--- a/install/include/install/wipe_data.h
+++ b/install/include/install/wipe_data.h
@@ -23,8 +23,6 @@
 
 struct selabel_handle;
 
-void SetWipeDataSehandle(selabel_handle* handle);
-
 // Returns true on success.
 bool WipeCache(RecoveryUI* ui, const std::function<bool()>& confirm);
 
diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp
index 7db7904..765a815 100644
--- a/install/wipe_data.cpp
+++ b/install/wipe_data.cpp
@@ -16,14 +16,11 @@
 
 #include "install/wipe_data.h"
 
-#include <dirent.h>
-#include <errno.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/stat.h>
 
 #include <functional>
-#include <memory>
 #include <vector>
 
 #include <android-base/file.h>
@@ -39,20 +36,6 @@
 constexpr const char* DATA_ROOT = "/data";
 constexpr const char* METADATA_ROOT = "/metadata";
 
-constexpr const char* CACHE_LOG_DIR = "/cache/recovery";
-
-static struct selabel_handle* sehandle;
-
-void SetWipeDataSehandle(selabel_handle* handle) {
-  sehandle = handle;
-}
-
-struct saved_log_file {
-  std::string name;
-  struct stat sb;
-  std::string data;
-};
-
 static bool EraseVolume(const char* volume, RecoveryUI* ui, bool convert_fbe) {
   bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
   bool is_data = (strcmp(volume, DATA_ROOT) == 0);
@@ -61,43 +44,10 @@
   ui->SetProgressType(RecoveryUI::INDETERMINATE);
 
   std::vector<saved_log_file> log_files;
-
   if (is_cache) {
-    // If we're reformatting /cache, we load any past logs
-    // (i.e. "/cache/recovery/last_*") and the current log
-    // ("/cache/recovery/log") into memory, so we can restore them after
-    // the reformat.
-
-    ensure_path_mounted(volume);
-
-    struct dirent* de;
-    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(CACHE_LOG_DIR), closedir);
-    if (d) {
-      while ((de = readdir(d.get())) != nullptr) {
-        if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) {
-          std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name);
-
-          struct stat sb;
-          if (stat(path.c_str(), &sb) == 0) {
-            // truncate files to 512kb
-            if (sb.st_size > (1 << 19)) {
-              sb.st_size = 1 << 19;
-            }
-
-            std::string data(sb.st_size, '\0');
-            FILE* f = fopen(path.c_str(), "rbe");
-            fread(&data[0], 1, data.size(), f);
-            fclose(f);
-
-            log_files.emplace_back(saved_log_file{ path, sb, data });
-          }
-        }
-      }
-    } else {
-      if (errno != ENOENT) {
-        PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR;
-      }
-    }
+    // If we're reformatting /cache, we load any past logs (i.e. "/cache/recovery/last_*") and the
+    // current log ("/cache/recovery/log") into memory, so we can restore them after the reformat.
+    log_files = ReadLogFilesToMemory();
   }
 
   ui->Print("Formatting %s...\n", volume);
@@ -128,24 +78,7 @@
   }
 
   if (is_cache) {
-    // Re-create the log dir and write back the log entries.
-    if (ensure_path_mounted(CACHE_LOG_DIR) == 0 &&
-        mkdir_recursively(CACHE_LOG_DIR, 0777, false, sehandle) == 0) {
-      for (const auto& log : log_files) {
-        if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid,
-                                              log.sb.st_gid)) {
-          PLOG(ERROR) << "Failed to write to " << log.name;
-        }
-      }
-    } else {
-      PLOG(ERROR) << "Failed to mount / create " << CACHE_LOG_DIR;
-    }
-
-    // Any part of the log we'd copied to cache is now gone.
-    // Reset the pointer so we copy from the beginning of the temp
-    // log.
-    reset_tmplog_offset();
-    copy_logs(true /* save_current_log */, true /* has_cache */, sehandle);
+    RestoreLogFilesAfterFormat(log_files);
   }
 
   return (result == 0);
diff --git a/otautil/include/otautil/logging.h b/otautil/include/otautil/logging.h
index c4f1329..6083497 100644
--- a/otautil/include/otautil/logging.h
+++ b/otautil/include/otautil/logging.h
@@ -18,9 +18,11 @@
 #define _LOGGING_H
 
 #include <stddef.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 
 #include <string>
+#include <vector>
 
 #include <log/log_id.h>
 
@@ -28,6 +30,14 @@
 
 struct selabel_handle;
 
+struct saved_log_file {
+  std::string name;
+  struct stat sb;
+  std::string data;
+};
+
+void SetLoggingSehandle(selabel_handle* handle);
+
 ssize_t logbasename(log_id_t id, char prio, const char* filename, const char* buf, size_t len,
                     void* arg);
 
@@ -48,4 +58,8 @@
 
 void save_kernel_log(const char* destination);
 
+std::vector<saved_log_file> ReadLogFilesToMemory();
+
+bool RestoreLogFilesAfterFormat(const std::vector<saved_log_file>& log_files);
+
 #endif  //_LOGGING_H
diff --git a/otautil/logging.cpp b/otautil/logging.cpp
index 7c330ac..484f115 100644
--- a/otautil/logging.cpp
+++ b/otautil/logging.cpp
@@ -16,17 +16,22 @@
 
 #include "otautil/logging.h"
 
+#include <dirent.h>
+#include <errno.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/klog.h>
 #include <sys/types.h>
 
+#include <algorithm>
+#include <memory>
 #include <string>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
 #include <private/android_filesystem_config.h> /* for AID_SYSTEM */
 #include <private/android_logger.h>            /* private pmsg functions */
 #include <selinux/label.h>
@@ -35,13 +40,21 @@
 #include "otautil/paths.h"
 #include "otautil/roots.h"
 
-static constexpr const char* LOG_FILE = "/cache/recovery/log";
-static constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install";
-static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
-static constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log";
+constexpr const char* LOG_FILE = "/cache/recovery/log";
+constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install";
+constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
+constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log";
 
-static const std::string LAST_KMSG_FILTER = "recovery/last_kmsg";
-static const std::string LAST_LOG_FILTER = "recovery/last_log";
+constexpr const char* LAST_KMSG_FILTER = "recovery/last_kmsg";
+constexpr const char* LAST_LOG_FILTER = "recovery/last_log";
+
+constexpr const char* CACHE_LOG_DIR = "/cache/recovery";
+
+static struct selabel_handle* logging_sehandle;
+
+void SetLoggingSehandle(selabel_handle* handle) {
+  logging_sehandle = handle;
+}
 
 // fopen(3)'s the given file, by mounting volumes and making parent dirs as necessary. Returns the
 // file pointer, or nullptr on error.
@@ -74,8 +87,8 @@
 ssize_t logbasename(log_id_t /* id */, char /* prio */, const char* filename, const char* /* buf */,
                     size_t len, void* arg) {
   bool* do_rotate = static_cast<bool*>(arg);
-  if (LAST_KMSG_FILTER.find(filename) != std::string::npos ||
-      LAST_LOG_FILTER.find(filename) != std::string::npos) {
+  if (std::string(LAST_KMSG_FILTER).find(filename) != std::string::npos ||
+      std::string(LAST_LOG_FILTER).find(filename) != std::string::npos) {
     *do_rotate = true;
   }
   return len;
@@ -92,8 +105,8 @@
   size_t dot = name.find_last_of('.');
   std::string sub = name.substr(0, dot);
 
-  if (LAST_KMSG_FILTER.find(sub) == std::string::npos &&
-      LAST_LOG_FILTER.find(sub) == std::string::npos) {
+  if (std::string(LAST_KMSG_FILTER).find(sub) == std::string::npos &&
+      std::string(LAST_LOG_FILTER).find(sub) == std::string::npos) {
     return __android_log_pmsg_file_write(id, prio, filename, buf, len);
   }
 
@@ -243,3 +256,70 @@
   buffer.resize(n);
   android::base::WriteStringToFile(buffer, destination);
 }
+
+std::vector<saved_log_file> ReadLogFilesToMemory() {
+  ensure_path_mounted("/cache");
+
+  struct dirent* de;
+  std::unique_ptr<DIR, decltype(&closedir)> d(opendir(CACHE_LOG_DIR), closedir);
+  if (!d) {
+    if (errno != ENOENT) {
+      PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR;
+    }
+    return {};
+  }
+
+  std::vector<saved_log_file> log_files;
+  while ((de = readdir(d.get())) != nullptr) {
+    if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) {
+      std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name);
+
+      struct stat sb;
+      if (stat(path.c_str(), &sb) != 0) {
+        PLOG(ERROR) << "Failed to stat " << path;
+        continue;
+      }
+      // Truncate files to 512kb
+      size_t read_size = std::min<size_t>(sb.st_size, 1 << 19);
+      std::string data(read_size, '\0');
+
+      android::base::unique_fd log_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY)));
+      if (log_fd == -1 || !android::base::ReadFully(log_fd, data.data(), read_size)) {
+        PLOG(ERROR) << "Failed to read log file " << path;
+        continue;
+      }
+
+      log_files.emplace_back(saved_log_file{ path, sb, data });
+    }
+  }
+
+  return log_files;
+}
+
+bool RestoreLogFilesAfterFormat(const std::vector<saved_log_file>& log_files) {
+  // Re-create the log dir and write back the log entries.
+  if (ensure_path_mounted(CACHE_LOG_DIR) != 0) {
+    PLOG(ERROR) << "Failed to mount " << CACHE_LOG_DIR;
+    return false;
+  }
+
+  if (mkdir_recursively(CACHE_LOG_DIR, 0777, false, logging_sehandle) != 0) {
+    PLOG(ERROR) << "Failed to create " << CACHE_LOG_DIR;
+    return false;
+  }
+
+  for (const auto& log : log_files) {
+    if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid,
+                                          log.sb.st_gid)) {
+      PLOG(ERROR) << "Failed to write to " << log.name;
+    }
+  }
+
+  // Any part of the log we'd copied to cache is now gone.
+  // Reset the pointer so we copy from the beginning of the temp
+  // log.
+  reset_tmplog_offset();
+  copy_logs(true /* save_current_log */, true /* has_cache */, logging_sehandle);
+
+  return true;
+}
diff --git a/recovery_main.cpp b/recovery_main.cpp
index 8f3f2a7..5f3ab76 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -435,7 +435,7 @@
     ui->Print("Warning: No file_contexts\n");
   }
 
-  SetWipeDataSehandle(sehandle);
+  SetLoggingSehandle(sehandle);
 
   std::atomic<Device::BuiltinAction> action;
   std::thread listener_thread(ListenRecoverySocket, ui, std::ref(action));