diff options
-rw-r--r-- | Android.bp | 8 | ||||
-rw-r--r-- | CleanSpec.mk | 10 | ||||
-rw-r--r-- | install/Android.bp | 7 | ||||
-rw-r--r-- | install/adb_install.cpp | 284 | ||||
-rw-r--r-- | install/fuse_sdcard_install.cpp | 4 | ||||
-rw-r--r-- | install/include/install/adb_install.h | 2 | ||||
-rw-r--r-- | install/include/install/fuse_sdcard_install.h | 2 | ||||
-rw-r--r-- | install/include/install/install.h | 9 | ||||
-rw-r--r-- | install/include/install/wipe_data.h | 30 | ||||
-rw-r--r-- | install/install.cpp | 18 | ||||
-rw-r--r-- | install/wipe_data.cpp | 123 | ||||
-rw-r--r-- | minadbd/Android.bp | 31 | ||||
-rw-r--r-- | minadbd/minadbd.cpp | 48 | ||||
-rw-r--r-- | minadbd/minadbd_services.cpp | 73 | ||||
-rw-r--r-- | minadbd/minadbd_services.h (renamed from minadbd/minadbd.h) | 7 | ||||
-rw-r--r-- | minadbd/minadbd_types.h | 53 | ||||
-rw-r--r-- | otautil/Android.bp | 1 | ||||
-rw-r--r-- | otautil/include/otautil/logging.h (renamed from logging.h) | 19 | ||||
-rw-r--r-- | otautil/logging.cpp (renamed from logging.cpp) | 125 | ||||
-rw-r--r-- | recovery-persist.cpp | 2 | ||||
-rw-r--r-- | recovery-refresh.cpp | 2 | ||||
-rw-r--r-- | recovery.cpp | 217 | ||||
-rw-r--r-- | recovery_main.cpp | 16 |
23 files changed, 771 insertions, 320 deletions
diff --git a/Android.bp b/Android.bp index a44a2c625..f92078256 100644 --- a/Android.bp +++ b/Android.bp @@ -69,6 +69,7 @@ cc_defaults { ], static_libs: [ + "libinstall", "librecovery_fastboot", "libminui", "libotautil", @@ -93,7 +94,6 @@ cc_library_static { ], shared_libs: [ - "libinstall", "librecovery_ui", ], } @@ -108,13 +108,10 @@ cc_binary { ], srcs: [ - "logging.cpp", "recovery_main.cpp", ], shared_libs: [ - "libinstall", - "libminadbd_services", "librecovery_ui", ], @@ -126,6 +123,7 @@ cc_binary { required: [ "e2fsdroid.recovery", "librecovery_ui_ext", + "minadbd", "mke2fs.conf.recovery", "mke2fs.recovery", "recovery_deps", @@ -141,7 +139,6 @@ cc_binary { ], srcs: [ - "logging.cpp", "recovery-persist.cpp", ], @@ -170,7 +167,6 @@ cc_binary { ], srcs: [ - "logging.cpp", "recovery-refresh.cpp", ], diff --git a/CleanSpec.mk b/CleanSpec.mk index fec823e7e..a7ab0d9be 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -44,9 +44,13 @@ #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) -# ************************************************ -# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST -# ************************************************ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/recovery_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libminui_intermediates/import_includes) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libinstall.recovery_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/system/lib64/libinstall.so) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ diff --git a/install/Android.bp b/install/Android.bp index aa47990ae..ea893a075 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -19,6 +19,10 @@ cc_defaults { "recovery_defaults", ], + header_libs: [ + "libminadbd_headers", + ], + shared_libs: [ "libbase", "libbootloader_message", @@ -47,7 +51,7 @@ cc_defaults { ], } -cc_library { +cc_library_static { name: "libinstall", recovery_available: true, @@ -62,6 +66,7 @@ cc_library { "install.cpp", "package.cpp", "verifier.cpp", + "wipe_data.cpp", ], shared_libs: [ diff --git a/install/adb_install.cpp b/install/adb_install.cpp index 5296ff608..548b6e5b9 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -21,97 +21,285 @@ #include <signal.h> #include <stdlib.h> #include <string.h> +#include <sys/epoll.h> +#include <sys/socket.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> +#include <atomic> +#include <functional> +#include <map> + +#include <android-base/file.h> #include <android-base/logging.h> +#include <android-base/memory.h> #include <android-base/properties.h> +#include <android-base/strings.h> +#include <android-base/unique_fd.h> #include "fuse_sideload.h" #include "install/install.h" +#include "minadbd_types.h" #include "recovery_ui/ui.h" +using CommandFunction = std::function<bool()>; + static bool SetUsbConfig(const std::string& state) { android::base::SetProperty("sys.usb.config", state); return android::base::WaitForProperty("sys.usb.state", state); } -int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) { - // Save the usb state to restore after the sideload operation. - std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); - // Clean up state and stop adbd. - if (usb_state != "none" && !SetUsbConfig("none")) { - LOG(ERROR) << "Failed to clear USB config"; - return INSTALL_ERROR; +// Parses the minadbd command in |message|; returns MinadbdCommands::kError upon errors. +static MinadbdCommands ParseMinadbdCommands(const std::string& message) { + if (!android::base::StartsWith(message, kMinadbdCommandPrefix)) { + LOG(ERROR) << "Failed to parse command in message " << message; + return MinadbdCommands::kError; } - ui->Print( - "\n\nNow send the package you want to apply\n" - "to the device with \"adb sideload <filename>\"...\n"); - - pid_t child; - if ((child = fork()) == 0) { - execl("/system/bin/recovery", "recovery", "--adbd", nullptr); - _exit(EXIT_FAILURE); + auto cmd_code_string = message.substr(strlen(kMinadbdCommandPrefix)); + auto cmd_code = android::base::get_unaligned<uint32_t>(cmd_code_string.c_str()); + if (cmd_code >= static_cast<uint32_t>(MinadbdCommands::kError)) { + LOG(ERROR) << "Unsupported command code: " << cmd_code; + return MinadbdCommands::kError; } - if (!SetUsbConfig("sideload")) { - LOG(ERROR) << "Failed to set usb config to sideload"; - return INSTALL_ERROR; - } + return static_cast<MinadbdCommands>(cmd_code); +} - // How long (in seconds) we wait for the host to start sending us a package, before timing out. - static constexpr int ADB_INSTALL_TIMEOUT = 300; +static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) { + char message[kMinadbdMessageSize]; + memcpy(message, kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix)); + android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), status); - // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host connects and starts serving a - // package. Poll for its appearance. (Note that inotify doesn't work with FUSE.) - int result = INSTALL_ERROR; - int status; - bool waited = false; - for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) { - if (waitpid(child, &status, WNOHANG) != 0) { - result = INSTALL_ERROR; - waited = true; - break; - } + if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) { + PLOG(ERROR) << "Failed to write message " << message; + return false; + } + return true; +} +// Installs the package from FUSE. Returns true if the installation succeeds, and false otherwise. +static bool AdbInstallPackageHandler(RecoveryUI* ui, int* result) { + // How long (in seconds) we wait for the package path to be ready. It doesn't need to be too long + // because the minadbd service has already issued an install command. FUSE_SIDELOAD_HOST_PATHNAME + // will start to exist once the host connects and starts serving a package. Poll for its + // appearance. (Note that inotify doesn't work with FUSE.) + constexpr int ADB_INSTALL_TIMEOUT = 15; + *result = INSTALL_ERROR; + for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) { struct stat st; if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) { if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) { sleep(1); continue; } else { - ui->Print("\nTimed out waiting for package.\n\n"); - result = INSTALL_ERROR; - kill(child, SIGKILL); + ui->Print("\nTimed out waiting for fuse to be ready.\n\n"); break; } } - result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0, ui); + *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui); break; } - if (!waited) { - // Calling stat() on this magic filename signals the minadbd subprocess to shut down. - struct stat st; - stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); + // Calling stat() on this magic filename signals the FUSE to exit. + struct stat st; + stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); + return *result == INSTALL_SUCCESS; +} + +// Parses and executes the command from minadbd. Returns false if we enter an invalid state so that +// the caller can kill the minadbd service properly. +static bool HandleMessageFromMinadbd( + int socket_fd, const std::map<MinadbdCommands, CommandFunction>& command_map) { + char buffer[kMinadbdMessageSize]; + if (!android::base::ReadFully(socket_fd, buffer, kMinadbdMessageSize)) { + PLOG(ERROR) << "Failed to read message from minadbd"; + return false; + } + + std::string message(buffer, buffer + kMinadbdMessageSize); + auto command_type = ParseMinadbdCommands(message); + if (command_type == MinadbdCommands::kError) { + return false; + } + if (command_map.find(command_type) == command_map.end()) { + LOG(ERROR) << "Unsupported command: " + << android::base::get_unaligned<unsigned int>( + message.substr(strlen(kMinadbdCommandPrefix)).c_str()); + return false; + } + + // We have received a valid command, execute the corresponding function. + const auto& command_func = command_map.at(command_type); + if (!command_func()) { + LOG(ERROR) << "Failed to execute command " << static_cast<unsigned int>(command_type); + return WriteStatusToFd(MinadbdCommandStatus::kFailure, socket_fd); + } + return WriteStatusToFd(MinadbdCommandStatus::kSuccess, socket_fd); +} + +// TODO(xunchang) add a wrapper function and kill the minadbd service there. +static void ListenAndExecuteMinadbdCommands( + pid_t minadbd_pid, android::base::unique_fd&& socket_fd, + const std::map<MinadbdCommands, CommandFunction>& command_map) { + android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC)); + if (epoll_fd == -1) { + PLOG(ERROR) << "Failed to create epoll"; + kill(minadbd_pid, SIGKILL); + return; + } + + constexpr int EPOLL_MAX_EVENTS = 10; + struct epoll_event ev = {}; + ev.events = EPOLLIN | EPOLLHUP; + ev.data.fd = socket_fd.get(); + struct epoll_event events[EPOLL_MAX_EVENTS]; + if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, socket_fd.get(), &ev) == -1) { + PLOG(ERROR) << "Failed to add socket fd to epoll"; + kill(minadbd_pid, SIGKILL); + return; + } + + // Set the timeout to be 300s when waiting for minadbd commands. + constexpr int TIMEOUT_MILLIS = 300 * 1000; + while (true) { + // Poll for the status change of the socket_fd, and handle the message if the fd is ready to + // read. + int event_count = + TEMP_FAILURE_RETRY(epoll_wait(epoll_fd.get(), events, EPOLL_MAX_EVENTS, TIMEOUT_MILLIS)); + if (event_count == -1) { + PLOG(ERROR) << "Failed to wait for epoll events"; + kill(minadbd_pid, SIGKILL); + return; + } + if (event_count == 0) { + LOG(ERROR) << "Timeout waiting for messages from minadbd"; + kill(minadbd_pid, SIGKILL); + return; + } + + for (int n = 0; n < event_count; n++) { + if (events[n].events & EPOLLHUP) { + LOG(INFO) << "Socket has been closed"; + kill(minadbd_pid, SIGKILL); + return; + } + if (!HandleMessageFromMinadbd(socket_fd.get(), command_map)) { + kill(minadbd_pid, SIGKILL); + return; + } + } + } +} + +// Recovery starts minadbd service as a child process, and spawns another thread to listen for the +// message from minadbd through a socket pair. Here is an example to execute one command from adb +// host. +// a. recovery b. listener thread c. minadbd service +// +// a1. create socket pair +// a2. fork minadbd service +// c3. wait for the adb commands +// from host +// c4. after receiving host commands: +// 1) set up pre-condition (i.e. +// start fuse for adb sideload) +// 2) issue command through +// socket. +// 3) wait for result +// a5. start listener thread +// b6. listen for message from +// minadbd in a loop. +// b7. After receiving a minadbd +// command from socket +// 1) execute the command function +// 2) send the result back to +// minadbd +// ...... +// c8. exit upon receiving the +// result +// a9. wait for listener thread +// to exit. +// +// a10. wait for minadbd to +// exit +// b11. exit the listening loop +// +static void CreateMinadbdServiceAndExecuteCommands( + const std::map<MinadbdCommands, CommandFunction>& command_map) { + signal(SIGPIPE, SIG_IGN); + + android::base::unique_fd recovery_socket; + android::base::unique_fd minadbd_socket; + if (!android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &recovery_socket, &minadbd_socket)) { + PLOG(ERROR) << "Failed to create socket"; + return; + } + + pid_t child = fork(); + if (child == -1) { + PLOG(ERROR) << "Failed to fork child process"; + return; + } + if (child == 0) { + recovery_socket.reset(); + execl("/system/bin/minadbd", "minadbd", "--socket_fd", + std::to_string(minadbd_socket.release()).c_str(), nullptr); + + _exit(EXIT_FAILURE); + } + + minadbd_socket.reset(); - // TODO: there should be a way to cancel waiting for a package (by pushing some button combo on - // the device). For now you just have to 'adb sideload' a file that's not a valid package, like - // "/dev/null". - waitpid(child, &status, 0); + // We need to call SetUsbConfig() after forking minadbd service. Because the function waits for + // the usb state to be updated, which depends on sys.usb.ffs.ready=1 set in the adb daemon. + if (!SetUsbConfig("sideload")) { + LOG(ERROR) << "Failed to set usb config to sideload"; + return; } + std::thread listener_thread(ListenAndExecuteMinadbdCommands, child, std::move(recovery_socket), + std::ref(command_map)); + + if (listener_thread.joinable()) { + listener_thread.join(); + } + + int status; + waitpid(child, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - if (WEXITSTATUS(status) == 3) { - ui->Print("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n"); + if (WEXITSTATUS(status) == MinadbdErrorCode::kMinadbdAdbVersionError) { + LOG(ERROR) << "\nYou need adb 1.0.32 or newer to sideload\nto this device.\n"; } else if (!WIFSIGNALED(status)) { - ui->Print("\n(adbd status %d)\n", WEXITSTATUS(status)); + LOG(ERROR) << "\n(adbd status " << WEXITSTATUS(status) << ")"; } } + signal(SIGPIPE, SIG_DFL); +} + +int apply_from_adb(RecoveryUI* ui) { + // Save the usb state to restore after the sideload operation. + std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); + // Clean up state and stop adbd. + if (usb_state != "none" && !SetUsbConfig("none")) { + LOG(ERROR) << "Failed to clear USB config"; + return INSTALL_ERROR; + } + + ui->Print( + "\n\nNow send the package you want to apply\n" + "to the device with \"adb sideload <filename>\"...\n"); + + int install_result = INSTALL_ERROR; + std::map<MinadbdCommands, CommandFunction> command_map{ + { MinadbdCommands::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) }, + }; + + CreateMinadbdServiceAndExecuteCommands(command_map); + // Clean up before switching to the older state, for example setting the state // to none sets sys/class/android_usb/android0/enable to 0. if (!SetUsbConfig("none")) { @@ -124,5 +312,5 @@ int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) { } } - return result; + return install_result; } diff --git a/install/fuse_sdcard_install.cpp b/install/fuse_sdcard_install.cpp index dde289f80..1aa8768e7 100644 --- a/install/fuse_sdcard_install.cpp +++ b/install/fuse_sdcard_install.cpp @@ -133,7 +133,7 @@ static bool StartSdcardFuse(const std::string& path) { return run_fuse_sideload(std::move(file_data_reader)) == 0; } -int ApplyFromSdcard(Device* device, bool* wipe_cache, RecoveryUI* ui) { +int ApplyFromSdcard(Device* device, RecoveryUI* ui) { if (ensure_path_mounted(SDCARD_ROOT) != 0) { LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n"; return INSTALL_ERROR; @@ -184,7 +184,7 @@ int ApplyFromSdcard(Device* device, bool* wipe_cache, RecoveryUI* ui) { } } - result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0 /*retry_count*/, ui); + result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /*retry_count*/, ui); break; } diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h index dbc824501..f7b065b64 100644 --- a/install/include/install/adb_install.h +++ b/install/include/install/adb_install.h @@ -18,4 +18,4 @@ #include <recovery_ui/ui.h> -int apply_from_adb(bool* wipe_cache, RecoveryUI* ui); +int apply_from_adb(RecoveryUI* ui); diff --git a/install/include/install/fuse_sdcard_install.h b/install/include/install/fuse_sdcard_install.h index 345aea45b..d9214ca3b 100644 --- a/install/include/install/fuse_sdcard_install.h +++ b/install/include/install/fuse_sdcard_install.h @@ -19,4 +19,4 @@ #include "recovery_ui/device.h" #include "recovery_ui/ui.h" -int ApplyFromSdcard(Device* device, bool* wipe_cache, RecoveryUI* ui); +int ApplyFromSdcard(Device* device, RecoveryUI* ui); diff --git a/install/include/install/install.h b/install/include/install/install.h index 74fb3d170..1e41b4843 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -43,10 +43,11 @@ enum class OtaType { BRICK, }; -// Installs the given update package. If INSTALL_SUCCESS is returned and *wipe_cache is true on -// exit, caller should wipe the cache partition. -int install_package(const std::string& package, bool* wipe_cache, bool needs_mount, int retry_count, - RecoveryUI* ui); +// Installs the given update package. This function should also wipe the cache partition after a +// successful installation if |should_wipe_cache| is true or an updater command asks to wipe the +// cache. +int install_package(const std::string& package, bool should_wipe_cache, bool needs_mount, + int retry_count, RecoveryUI* ui); // Verifies the package by ota keys. Returns true if the package is verified successfully, // otherwise returns false. diff --git a/install/include/install/wipe_data.h b/install/include/install/wipe_data.h new file mode 100644 index 000000000..b34891f3d --- /dev/null +++ b/install/include/install/wipe_data.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <functional> + +#include "recovery_ui/device.h" +#include "recovery_ui/ui.h" + +struct selabel_handle; + +// Returns true on success. +bool WipeCache(RecoveryUI* ui, const std::function<bool()>& confirm); + +// Returns true on success. +bool WipeData(Device* device, bool convert_fbe); diff --git a/install/install.cpp b/install/install.cpp index a1124c361..e2d470096 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -48,6 +48,7 @@ #include "install/package.h" #include "install/verifier.h" +#include "install/wipe_data.h" #include "otautil/error_code.h" #include "otautil/paths.h" #include "otautil/roots.h" @@ -631,10 +632,9 @@ static int really_install_package(const std::string& path, bool* wipe_cache, boo return result; } -int install_package(const std::string& path, bool* wipe_cache, bool needs_mount, int retry_count, - RecoveryUI* ui) { +int install_package(const std::string& path, bool should_wipe_cache, bool needs_mount, + int retry_count, RecoveryUI* ui) { CHECK(!path.empty()); - CHECK(wipe_cache != nullptr); auto start = std::chrono::system_clock::now(); @@ -647,8 +647,10 @@ int install_package(const std::string& path, bool* wipe_cache, bool needs_mount, LOG(ERROR) << "failed to set up expected mounts for install; aborting"; result = INSTALL_ERROR; } else { - result = really_install_package(path, wipe_cache, needs_mount, &log_buffer, retry_count, - &max_temperature, ui); + bool updater_wipe_cache = false; + result = really_install_package(path, &updater_wipe_cache, needs_mount, &log_buffer, + retry_count, &max_temperature, ui); + should_wipe_cache = should_wipe_cache || updater_wipe_cache; } // Measure the time spent to apply OTA update in seconds. @@ -703,6 +705,12 @@ int install_package(const std::string& path, bool* wipe_cache, bool needs_mount, // Write a copy into last_log. LOG(INFO) << log_content; + if (result == INSTALL_SUCCESS && should_wipe_cache) { + if (!WipeCache(ui, nullptr)) { + result = INSTALL_ERROR; + } + } + return result; } diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp new file mode 100644 index 000000000..765a8152b --- /dev/null +++ b/install/wipe_data.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "install/wipe_data.h" + +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> + +#include <functional> +#include <vector> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/stringprintf.h> + +#include "otautil/dirutil.h" +#include "otautil/logging.h" +#include "otautil/roots.h" +#include "recovery_ui/ui.h" + +constexpr const char* CACHE_ROOT = "/cache"; +constexpr const char* DATA_ROOT = "/data"; +constexpr const char* METADATA_ROOT = "/metadata"; + +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); + + ui->SetBackground(RecoveryUI::ERASING); + 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. + log_files = ReadLogFilesToMemory(); + } + + ui->Print("Formatting %s...\n", volume); + + ensure_path_unmounted(volume); + + int result; + if (is_data && convert_fbe) { + constexpr const char* CONVERT_FBE_DIR = "/tmp/convert_fbe"; + constexpr const char* CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe"; + // Create convert_fbe breadcrumb file to signal init to convert to file based encryption, not + // full disk encryption. + if (mkdir(CONVERT_FBE_DIR, 0700) != 0) { + PLOG(ERROR) << "Failed to mkdir " << CONVERT_FBE_DIR; + return false; + } + FILE* f = fopen(CONVERT_FBE_FILE, "wbe"); + if (!f) { + PLOG(ERROR) << "Failed to convert to file encryption"; + return false; + } + fclose(f); + result = format_volume(volume, CONVERT_FBE_DIR); + remove(CONVERT_FBE_FILE); + rmdir(CONVERT_FBE_DIR); + } else { + result = format_volume(volume); + } + + if (is_cache) { + RestoreLogFilesAfterFormat(log_files); + } + + return (result == 0); +} + +bool WipeCache(RecoveryUI* ui, const std::function<bool()>& confirm_func) { + bool has_cache = volume_for_mount_point("/cache") != nullptr; + if (!has_cache) { + ui->Print("No /cache partition found.\n"); + return false; + } + + if (confirm_func && !confirm_func()) { + return false; + } + + ui->Print("\n-- Wiping cache...\n"); + bool success = EraseVolume("/cache", ui, false); + ui->Print("Cache wipe %s.\n", success ? "complete" : "failed"); + return success; +} + +bool WipeData(Device* device, bool convert_fbe) { + RecoveryUI* ui = device->GetUI(); + ui->Print("\n-- Wiping data...\n"); + bool success = device->PreWipeData(); + if (success) { + success &= EraseVolume(DATA_ROOT, ui, convert_fbe); + bool has_cache = volume_for_mount_point("/cache") != nullptr; + if (has_cache) { + success &= EraseVolume(CACHE_ROOT, ui, false); + } + if (volume_for_mount_point(METADATA_ROOT) != nullptr) { + success &= EraseVolume(METADATA_ROOT, ui, false); + } + } + if (success) { + success &= device->PostWipeData(); + } + ui->Print("Data wipe %s.\n", success ? "complete" : "failed"); + return success; +}
\ No newline at end of file diff --git a/minadbd/Android.bp b/minadbd/Android.bp index 9b889f4a2..e4f7712e5 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -40,7 +40,6 @@ cc_library { srcs: [ "fuse_adb_provider.cpp", - "minadbd.cpp", "minadbd_services.cpp", ], @@ -52,6 +51,36 @@ cc_library { ], } +cc_library_headers { + name: "libminadbd_headers", + recovery_available: true, + // TODO create a include dir + export_include_dirs: [ + ".", + ], +} + +cc_binary { + name: "minadbd", + recovery: true, + + defaults: [ + "minadbd_defaults", + ], + + srcs: [ + "minadbd.cpp", + ], + + shared_libs: [ + "libadbd", + "libbase", + "libcrypto", + "libfusesideload", + "libminadbd_services", + ], +} + cc_test { name: "minadbd_test", isolated: true, diff --git a/minadbd/minadbd.cpp b/minadbd/minadbd.cpp index 349189cc7..57158ad57 100644 --- a/minadbd/minadbd.cpp +++ b/minadbd/minadbd.cpp @@ -14,30 +14,54 @@ * limitations under the License. */ -#include "minadbd.h" - #include <errno.h> +#include <fcntl.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> +#include <strings.h> + +#include <android-base/logging.h> +#include <android-base/parseint.h> #include "adb.h" #include "adb_auth.h" #include "transport.h" -int minadbd_main() { - adb_device_banner = "sideload"; +#include "minadbd_services.h" +#include "minadbd_types.h" + +int main(int argc, char** argv) { + android::base::InitLogging(argv, &android::base::StderrLogger); + // TODO(xunchang) implement a command parser + if (argc != 3 || strcmp("--socket_fd", argv[1]) != 0) { + LOG(ERROR) << "minadbd has invalid arguments, argc: " << argc; + exit(kMinadbdArgumentsParsingError); + } + + int socket_fd; + if (!android::base::ParseInt(argv[2], &socket_fd)) { + LOG(ERROR) << "Failed to parse int in " << argv[2]; + exit(kMinadbdArgumentsParsingError); + } + if (fcntl(socket_fd, F_GETFD, 0) == -1) { + PLOG(ERROR) << "Failed to get minadbd socket"; + exit(kMinadbdSocketIOError); + } + SetMinadbdSocketFd(socket_fd); + + adb_device_banner = "sideload"; - signal(SIGPIPE, SIG_IGN); + signal(SIGPIPE, SIG_IGN); - // We can't require authentication for sideloading. http://b/22025550. - auth_required = false; + // We can't require authentication for sideloading. http://b/22025550. + auth_required = false; - init_transport_registration(); - usb_init(); + init_transport_registration(); + usb_init(); - VLOG(ADB) << "Event loop starting"; - fdevent_loop(); + VLOG(ADB) << "Event loop starting"; + fdevent_loop(); - return 0; + return 0; } diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 6fe5c79bc..79e6fc4e0 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "minadbd_services.h" + #include <errno.h> #include <inttypes.h> #include <stdio.h> @@ -27,38 +29,95 @@ #include <string_view> #include <thread> +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/memory.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> + #include "adb.h" #include "adb_unique_fd.h" #include "fdevent.h" #include "fuse_adb_provider.h" #include "fuse_sideload.h" +#include "minadbd_types.h" #include "services.h" #include "sysdeps.h" +static int minadbd_socket = -1; +void SetMinadbdSocketFd(int socket_fd) { + minadbd_socket = socket_fd; +} + +static bool WriteCommandToFd(MinadbdCommands cmd, int fd) { + char message[kMinadbdMessageSize]; + memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix)); + android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), cmd); + + if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) { + PLOG(ERROR) << "Failed to write message " << message; + return false; + } + return true; +} + +// Blocks and reads the command status from |fd|. Returns false if the received message has a +// format error. +static bool WaitForCommandStatus(int fd, MinadbdCommandStatus* status) { + char buffer[kMinadbdMessageSize]; + if (!android::base::ReadFully(fd, buffer, kMinadbdMessageSize)) { + PLOG(ERROR) << "Failed to response status from socket"; + exit(kMinadbdSocketIOError); + } + + std::string message(buffer, buffer + kMinadbdMessageSize); + if (!android::base::StartsWith(message, kMinadbdStatusPrefix)) { + LOG(ERROR) << "Failed to parse status in " << message; + return false; + } + + *status = android::base::get_unaligned<MinadbdCommandStatus>( + message.substr(strlen(kMinadbdStatusPrefix)).c_str()); + return true; +} + static void sideload_host_service(unique_fd sfd, const std::string& args) { int64_t file_size; int block_size; if ((sscanf(args.c_str(), "%" SCNd64 ":%d", &file_size, &block_size) != 2) || file_size <= 0 || block_size <= 0) { - printf("bad sideload-host arguments: %s\n", args.c_str()); - exit(1); + LOG(ERROR) << "bad sideload-host arguments: " << args; + exit(kMinadbdPackageSizeError); } - printf("sideload-host file size %" PRId64 " block size %d\n", file_size, block_size); + LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size; + + if (!WriteCommandToFd(MinadbdCommands::kInstall, minadbd_socket)) { + exit(kMinadbdSocketIOError); + } auto adb_data_reader = std::make_unique<FuseAdbDataProvider>(std::move(sfd), file_size, block_size); - int result = run_fuse_sideload(std::move(adb_data_reader)); + if (int result = run_fuse_sideload(std::move(adb_data_reader)); result != 0) { + LOG(ERROR) << "Failed to start fuse"; + exit(kMinadbdFuseStartError); + } + + MinadbdCommandStatus status; + if (!WaitForCommandStatus(minadbd_socket, &status)) { + exit(kMinadbdMessageFormatError); + } + LOG(INFO) << "Got command status: " << static_cast<unsigned int>(status); - printf("sideload_host finished\n"); - exit(result == 0 ? 0 : 1); + LOG(INFO) << "sideload_host finished"; + exit(kMinadbdSuccess); } unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) { if (name.starts_with("sideload:")) { // This exit status causes recovery to print a special error message saying to use a newer adb // (that supports sideload-host). - exit(3); + exit(kMinadbdAdbVersionError); } else if (name.starts_with("sideload-host:")) { std::string arg(name.substr(strlen("sideload-host:"))); return create_service_thread("sideload-host", diff --git a/minadbd/minadbd.h b/minadbd/minadbd_services.h index 3570a5da5..6835bd770 100644 --- a/minadbd/minadbd.h +++ b/minadbd/minadbd_services.h @@ -14,9 +14,6 @@ * limitations under the License. */ -#ifndef MINADBD_H__ -#define MINADBD_H__ +#pragma once -int minadbd_main(); - -#endif +void SetMinadbdSocketFd(int socket_fd); diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h new file mode 100644 index 000000000..7bd69096a --- /dev/null +++ b/minadbd/minadbd_types.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <stdint.h> + +// The message between recovery and minadbd is 8 bytes in size unless the length is explicitly +// specified. Both the command and status has the format |prefix(4 bytes) + encoded enum(4 bytes)|. +constexpr size_t kMinadbdMessageSize = 8; +constexpr char const kMinadbdCommandPrefix[] = "COMD"; +constexpr char const kMinadbdStatusPrefix[] = "STAT"; + +enum MinadbdErrorCode : int { + kMinadbdSuccess = 0, + kMinadbdArgumentsParsingError = 1, + kMinadbdSocketIOError = 2, + kMinadbdMessageFormatError = 3, + kMinadbdAdbVersionError = 4, + kMinadbdPackageSizeError = 5, + kMinadbdFuseStartError = 6, + kMinadbdUnsupportedCommandError = 7, + kMinadbdCommandExecutionError = 8, + kMinadbdErrorUnknown = 9, +}; + +enum class MinadbdCommandStatus : uint32_t { + kSuccess = 0, + kFailure = 1, +}; + +enum class MinadbdCommands : uint32_t { + kInstall = 0, + kUiPrint = 1, + kError = 2, +}; + +static_assert(kMinadbdMessageSize == sizeof(kMinadbdCommandPrefix) - 1 + sizeof(MinadbdCommands)); +static_assert(kMinadbdMessageSize == + sizeof(kMinadbdStatusPrefix) - 1 + sizeof(MinadbdCommandStatus)); diff --git a/otautil/Android.bp b/otautil/Android.bp index b4936c08b..0a21731e8 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -40,6 +40,7 @@ cc_library_static { android: { srcs: [ "dirutil.cpp", + "logging.cpp", "mounts.cpp", "parse_install_logs.cpp", "roots.cpp", diff --git a/logging.h b/otautil/include/otautil/logging.h index 3cfbc7af6..608349785 100644 --- a/logging.h +++ b/otautil/include/otautil/logging.h @@ -18,14 +18,26 @@ #define _LOGGING_H #include <stddef.h> +#include <sys/stat.h> #include <sys/types.h> #include <string> +#include <vector> #include <log/log_id.h> static constexpr int KEEP_LOG_COUNT = 10; +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); @@ -41,10 +53,13 @@ void rotate_logs(const char* last_log_file, const char* last_kmsg_file); void check_and_fclose(FILE* fp, const std::string& name); void copy_log_file_to_pmsg(const std::string& source, const std::string& destination); -void copy_log_file(const std::string& source, const std::string& destination, bool append); -void copy_logs(bool modified_flash, bool has_cache); +void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle); void reset_tmplog_offset(); 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/logging.cpp b/otautil/logging.cpp index 48f9ec317..484f1150f 100644 --- a/logging.cpp +++ b/otautil/logging.cpp @@ -14,38 +14,51 @@ * limitations under the License. */ -#include "logging.h" +#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> -#include "common.h" #include "otautil/dirutil.h" #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. -static FILE* fopen_path(const std::string& path, const char* mode) { +static FILE* fopen_path(const std::string& path, const char* mode, const selabel_handle* sehandle) { if (ensure_path_mounted(path) != 0) { LOG(ERROR) << "Can't mount " << path; return nullptr; @@ -74,8 +87,8 @@ void check_and_fclose(FILE* fp, const std::string& name) { 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 @@ ssize_t logrotate(log_id_t id, char prio, const char* filename, const char* buf, 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); } @@ -165,8 +178,9 @@ void reset_tmplog_offset() { tmplog_offset = 0; } -void copy_log_file(const std::string& source, const std::string& destination, bool append) { - FILE* dest_fp = fopen_path(destination, append ? "ae" : "we"); +static void copy_log_file(const std::string& source, const std::string& destination, bool append, + const selabel_handle* sehandle) { + FILE* dest_fp = fopen_path(destination, append ? "ae" : "we", sehandle); if (dest_fp == nullptr) { PLOG(ERROR) << "Can't open " << destination; } else { @@ -189,11 +203,11 @@ void copy_log_file(const std::string& source, const std::string& destination, bo } } -void copy_logs(bool modified_flash, bool has_cache) { - // We only rotate and record the log of the current session if there are actual attempts to modify - // the flash, such as wipes, installs from BCB or menu selections. This is to avoid unnecessary +void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle) { + // We only rotate and record the log of the current session if explicitly requested. This usually + // happens after wipes, installation from BCB or menu selections. This is to avoid unnecessary // rotation (and possible deletion) of log files, if it does not do anything loggable. - if (!modified_flash) { + if (!save_current_log) { return; } @@ -211,9 +225,9 @@ void copy_logs(bool modified_flash, bool has_cache) { rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE); // Copy logs to cache so the system can find out what happened. - copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true); - copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false); - copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false); + copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true, sehandle); + copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false, sehandle); + copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false, sehandle); save_kernel_log(LAST_KMSG_FILE); chmod(LOG_FILE, 0600); chown(LOG_FILE, AID_SYSTEM, AID_SYSTEM); @@ -242,3 +256,70 @@ void save_kernel_log(const char* destination) { 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-persist.cpp b/recovery-persist.cpp index e2a6699f6..294017a12 100644 --- a/recovery-persist.cpp +++ b/recovery-persist.cpp @@ -43,7 +43,7 @@ #include <metricslogger/metrics_logger.h> #include <private/android_logger.h> /* private pmsg functions */ -#include "logging.h" +#include "otautil/logging.h" #include "otautil/parse_install_logs.h" constexpr const char* LAST_LOG_FILE = "/data/misc/recovery/last_log"; diff --git a/recovery-refresh.cpp b/recovery-refresh.cpp index aee1ca592..d41755d0a 100644 --- a/recovery-refresh.cpp +++ b/recovery-refresh.cpp @@ -42,7 +42,7 @@ #include <private/android_logger.h> /* private pmsg functions */ -#include "logging.h" +#include "otautil/logging.h" int main(int argc, char **argv) { static const char filter[] = "recovery/"; diff --git a/recovery.cpp b/recovery.cpp index 034918498..0e6e4976d 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -27,7 +27,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/stat.h> #include <sys/types.h> #include <unistd.h> @@ -55,30 +54,27 @@ #include "install/fuse_sdcard_install.h" #include "install/install.h" #include "install/package.h" -#include "logging.h" -#include "otautil/dirutil.h" +#include "install/wipe_data.h" #include "otautil/error_code.h" +#include "otautil/logging.h" #include "otautil/paths.h" #include "otautil/roots.h" #include "otautil/sysutil.h" #include "recovery_ui/screen_ui.h" #include "recovery_ui/ui.h" -static constexpr const char* CACHE_LOG_DIR = "/cache/recovery"; static constexpr const char* COMMAND_FILE = "/cache/recovery/command"; static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg"; static constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log"; static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale"; static constexpr const char* CACHE_ROOT = "/cache"; -static constexpr const char* DATA_ROOT = "/data"; -static constexpr const char* METADATA_ROOT = "/metadata"; // We define RECOVERY_API_VERSION in Android.mk, which will be picked up by build system and packed // into target_files.zip. Assert the version defined in code and in Android.mk are consistent. static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions."); -static bool modified_flash = false; +static bool save_current_log = false; std::string stage; const char* reason = nullptr; @@ -148,7 +144,7 @@ static void finish_recovery() { } } - copy_logs(modified_flash, has_cache); + copy_logs(save_current_log, has_cache, sehandle); // Reset to normal system boot so recovery won't cycle indefinitely. std::string err; @@ -167,110 +163,6 @@ static void finish_recovery() { sync(); // For good measure. } -struct saved_log_file { - std::string name; - struct stat sb; - std::string data; -}; - -static bool erase_volume(const char* volume) { - bool is_cache = (strcmp(volume, CACHE_ROOT) == 0); - bool is_data = (strcmp(volume, DATA_ROOT) == 0); - - ui->SetBackground(RecoveryUI::ERASING); - 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; - } - } - } - - ui->Print("Formatting %s...\n", volume); - - ensure_path_unmounted(volume); - - int result; - if (is_data && reason && strcmp(reason, "convert_fbe") == 0) { - static constexpr const char* CONVERT_FBE_DIR = "/tmp/convert_fbe"; - static constexpr const char* CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe"; - // Create convert_fbe breadcrumb file to signal init to convert to file based encryption, not - // full disk encryption. - if (mkdir(CONVERT_FBE_DIR, 0700) != 0) { - PLOG(ERROR) << "Failed to mkdir " << CONVERT_FBE_DIR; - return false; - } - FILE* f = fopen(CONVERT_FBE_FILE, "wbe"); - if (!f) { - PLOG(ERROR) << "Failed to convert to file encryption"; - return false; - } - fclose(f); - result = format_volume(volume, CONVERT_FBE_DIR); - remove(CONVERT_FBE_FILE); - rmdir(CONVERT_FBE_DIR); - } else { - result = format_volume(volume); - } - - 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(modified_flash, has_cache); - } - - return (result == 0); -} - static bool yes_no(Device* device, const char* question1, const char* question2) { std::vector<std::string> headers{ question1, question2 }; std::vector<std::string> items{ " No", " Yes" }; @@ -292,28 +184,6 @@ static bool ask_to_wipe_data(Device* device) { return (chosen_item == 1); } -// Return true on success. -static bool wipe_data(Device* device) { - modified_flash = true; - - ui->Print("\n-- Wiping data...\n"); - bool success = device->PreWipeData(); - if (success) { - success &= erase_volume(DATA_ROOT); - if (has_cache) { - success &= erase_volume(CACHE_ROOT); - } - if (volume_for_mount_point(METADATA_ROOT) != nullptr) { - success &= erase_volume(METADATA_ROOT); - } - } - if (success) { - success &= device->PostWipeData(); - } - ui->Print("Data wipe %s.\n", success ? "complete" : "failed"); - return success; -} - static InstallResult prompt_and_wipe_data(Device* device) { // Use a single string and let ScreenRecoveryUI handles the wrapping. std::vector<std::string> wipe_data_menu_headers{ @@ -341,7 +211,8 @@ static InstallResult prompt_and_wipe_data(Device* device) { } if (ask_to_wipe_data(device)) { - if (wipe_data(device)) { + bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0; + if (WipeData(device, convert_fbe)) { return INSTALL_SUCCESS; } else { return INSTALL_ERROR; @@ -350,25 +221,6 @@ static InstallResult prompt_and_wipe_data(Device* device) { } } -// Return true on success. -static bool wipe_cache(bool should_confirm, Device* device) { - if (!has_cache) { - ui->Print("No /cache partition found.\n"); - return false; - } - - if (should_confirm && !yes_no(device, "Wipe cache?", " THIS CAN NOT BE UNDONE!")) { - return false; - } - - modified_flash = true; - - ui->Print("\n-- Wiping cache...\n"); - bool success = erase_volume("/cache"); - ui->Print("Cache wipe %s.\n", success ? "complete" : "failed"); - return success; -} - // Secure-wipe a given partition. It uses BLKSECDISCARD, if supported. Otherwise, it goes with // BLKDISCARD (if device supports BLKDISCARDZEROES) or BLKZEROOUT. static bool secure_wipe_partition(const std::string& partition) { @@ -653,7 +505,6 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { ? Device::REBOOT : device->InvokeMenuItem(chosen_item); - bool should_wipe_cache = false; switch (chosen_action) { case Device::NO_ACTION: break; @@ -666,41 +517,40 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { return chosen_action; case Device::WIPE_DATA: + save_current_log = true; if (ui->IsTextVisible()) { if (ask_to_wipe_data(device)) { - wipe_data(device); + WipeData(device, false); } } else { - wipe_data(device); + WipeData(device, false); return Device::NO_ACTION; } break; - case Device::WIPE_CACHE: - wipe_cache(ui->IsTextVisible(), device); + case Device::WIPE_CACHE: { + save_current_log = true; + std::function<bool()> confirm_func = [&device]() { + return yes_no(device, "Wipe cache?", " THIS CAN NOT BE UNDONE!"); + }; + WipeCache(ui, ui->IsTextVisible() ? confirm_func : nullptr); if (!ui->IsTextVisible()) return Device::NO_ACTION; break; - + } case Device::APPLY_ADB_SIDELOAD: case Device::APPLY_SDCARD: { - modified_flash = true; + save_current_log = true; bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD); if (adb) { - status = apply_from_adb(&should_wipe_cache, ui); + status = apply_from_adb(ui); } else { - status = ApplyFromSdcard(device, &should_wipe_cache, ui); - } - - if (status == INSTALL_SUCCESS && should_wipe_cache) { - if (!wipe_cache(false, device)) { - status = INSTALL_ERROR; - } + status = ApplyFromSdcard(device, ui); } if (status != INSTALL_SUCCESS) { ui->SetBackground(RecoveryUI::ERROR); ui->Print("Installation aborted.\n"); - copy_logs(modified_flash, has_cache); + copy_logs(save_current_log, has_cache, sehandle); } else if (!ui->IsTextVisible()) { return Device::NO_ACTION; // reboot if logs aren't visible } else { @@ -987,7 +837,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri if (update_package != nullptr) { // It's not entirely true that we will modify the flash. But we want // to log the update attempt since update_package is non-NULL. - modified_flash = true; + save_current_log = true; int required_battery_level; if (retry_count == 0 && !is_battery_ok(&required_battery_level)) { @@ -1009,11 +859,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri set_retry_bootloader_message(retry_count + 1, args); } - modified_flash = true; - status = install_package(update_package, &should_wipe_cache, true, retry_count, ui); - if (status == INSTALL_SUCCESS && should_wipe_cache) { - wipe_cache(false, device); - } + status = install_package(update_package, should_wipe_cache, true, retry_count, ui); if (status != INSTALL_SUCCESS) { ui->Print("Installation aborted.\n"); @@ -1021,7 +867,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri // RETRY_LIMIT times before we abandon this OTA update. static constexpr int RETRY_LIMIT = 4; if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) { - copy_logs(modified_flash, has_cache); + copy_logs(save_current_log, has_cache, sehandle); retry_count += 1; set_retry_bootloader_message(retry_count, args); // Print retry count on screen. @@ -1045,12 +891,14 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri } } } else if (should_wipe_data) { - if (!wipe_data(device)) { + save_current_log = true; + bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0; + if (!WipeData(device, convert_fbe)) { status = INSTALL_ERROR; } } else if (should_prompt_and_wipe_data) { // Trigger the logging to capture the cause, even if user chooses to not wipe data. - modified_flash = true; + save_current_log = true; ui->ShowText(true); ui->SetBackground(RecoveryUI::ERROR); @@ -1059,7 +907,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri ui->ShowText(false); } } else if (should_wipe_cache) { - if (!wipe_cache(false, device)) { + save_current_log = true; + if (!WipeCache(ui, nullptr)) { status = INSTALL_ERROR; } } else if (should_wipe_ab) { @@ -1073,15 +922,11 @@ Device::BuiltinAction start_recovery(Device* device, const std::vector<std::stri // sideload finishes even if there are errors. Unless one turns on the // text display during the installation. This is to enable automated // testing. + save_current_log = true; if (!sideload_auto_reboot) { ui->ShowText(true); } - status = apply_from_adb(&should_wipe_cache, ui); - if (status == INSTALL_SUCCESS && should_wipe_cache) { - if (!wipe_cache(false, device)) { - status = INSTALL_ERROR; - } - } + status = apply_from_adb(ui); ui->Print("\nInstall from ADB complete (status: %d).\n", status); if (sideload_auto_reboot) { ui->Print("Rebooting automatically.\n"); diff --git a/recovery_main.cpp b/recovery_main.cpp index b41368d7b..5f3ab76dd 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -50,8 +50,8 @@ #include "common.h" #include "fastboot/fastboot.h" -#include "logging.h" -#include "minadbd/minadbd.h" +#include "install/wipe_data.h" +#include "otautil/logging.h" #include "otautil/paths.h" #include "otautil/roots.h" #include "otautil/sysutil.h" @@ -322,16 +322,6 @@ int main(int argc, char** argv) { // Take action to refresh pmsg contents __android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logrotate, &do_rotate); - // If this binary is started with the single argument "--adbd", instead of being the normal - // recovery binary, it turns into kind of a stripped-down version of adbd that only supports the - // 'sideload' command. Note this must be a real argument, not anything in the command file or - // bootloader control block; the only way recovery should be run with this argument is when it - // starts a copy of itself from the apply_from_adb() function. - if (argc == 2 && strcmp(argv[1], "--adbd") == 0) { - minadbd_main(); - return 0; - } - time_t start = time(nullptr); // redirect_stdio should be called only in non-sideload mode. Otherwise we may have two logger @@ -445,6 +435,8 @@ int main(int argc, char** argv) { ui->Print("Warning: No file_contexts\n"); } + SetLoggingSehandle(sehandle); + std::atomic<Device::BuiltinAction> action; std::thread listener_thread(ListenRecoverySocket, ui, std::ref(action)); listener_thread.detach(); |