diff options
-rw-r--r-- | common.h | 11 | ||||
-rw-r--r-- | install.cpp | 2 | ||||
-rw-r--r-- | install.h | 3 | ||||
-rw-r--r-- | print_sha1.h | 4 | ||||
-rw-r--r-- | recovery.cpp | 110 | ||||
-rw-r--r-- | roots.h | 2 | ||||
-rw-r--r-- | tests/component/updater_test.cpp | 83 | ||||
-rw-r--r-- | updater/install.cpp | 37 | ||||
-rw-r--r-- | verifier.cpp | 7 |
9 files changed, 174 insertions, 85 deletions
@@ -17,15 +17,22 @@ #ifndef RECOVERY_COMMON_H #define RECOVERY_COMMON_H -#include <stdbool.h> #include <stdio.h> #include <stdarg.h> #define STRINGIFY(x) #x #define EXPAND(x) STRINGIFY(x) +class RecoveryUI; + +extern RecoveryUI* ui; extern bool modified_flash; -typedef struct fstab_rec Volume; + +// The current stage, e.g. "1/2". +extern const char* stage; + +// The reason argument provided in "--reason=". +extern const char* reason; // fopen a file, mounting volumes and making parent dirs as necessary. FILE* fopen_path(const char *path, const char *mode); diff --git a/install.cpp b/install.cpp index 0f9088a7a..3871271f7 100644 --- a/install.cpp +++ b/install.cpp @@ -43,8 +43,6 @@ #include "ui.h" #include "verifier.h" -extern RecoveryUI* ui; - #define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" #define PUBLIC_KEYS_FILE "/res/keys" static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata"; @@ -20,10 +20,9 @@ #include <string> #include <ziparchive/zip_archive.h> -#include "common.h" - enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE, INSTALL_SKIPPED, INSTALL_RETRY }; + // Install the package specified by root_path. If INSTALL_SUCCESS is // returned and *wipe_cache is true on exit, caller should wipe the // cache partition. diff --git a/print_sha1.h b/print_sha1.h index c7c1f3651..1f8589519 100644 --- a/print_sha1.h +++ b/print_sha1.h @@ -20,7 +20,7 @@ #include <stdint.h> #include <string> -#include "openssl/sha.h" +#include <openssl/sha.h> static std::string print_sha1(const uint8_t* sha1, size_t len) { const char* hex = "0123456789abcdef"; @@ -41,7 +41,7 @@ static std::string short_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) { } static std::string print_hex(const uint8_t* bytes, size_t len) { - return print_sha1(bytes, len); + return print_sha1(bytes, len); } #endif // RECOVERY_PRINT_SHA1_H diff --git a/recovery.cpp b/recovery.cpp index bb7a15b6c..02d460eb9 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -70,8 +70,6 @@ #include "screen_ui.h" #include "ui.h" -struct selabel_handle *sehandle; - static const struct option OPTIONS[] = { { "update_package", required_argument, NULL, 'u' }, { "retry_count", required_argument, NULL, 'n' }, @@ -119,15 +117,18 @@ static const int BATTERY_READ_TIMEOUT_IN_SEC = 10; // So we should check battery with a slightly lower limitation. static const int BATTERY_OK_PERCENTAGE = 20; static const int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15; -constexpr const char* RECOVERY_WIPE = "/etc/recovery.wipe"; +static constexpr const char* RECOVERY_WIPE = "/etc/recovery.wipe"; +static constexpr const char* DEFAULT_LOCALE = "en_US"; -RecoveryUI* ui = NULL; -static const char* locale = "en_US"; -char* stage = NULL; -char* reason = NULL; -bool modified_flash = false; +static std::string locale; static bool has_cache = false; +RecoveryUI* ui = nullptr; +bool modified_flash = false; +const char* stage = nullptr; +const char* reason = nullptr; +struct selabel_handle* sehandle; + /* * The recovery tool communicates with the main system through /cache files. * /cache/recovery/command - INPUT - command line for tool, one arg per line @@ -206,6 +207,9 @@ FILE* fopen_path(const char *path, const char *mode) { // close a file, log an error if the error indicator is set static void check_and_fclose(FILE *fp, const char *name) { fflush(fp); + if (fsync(fileno(fp)) == -1) { + PLOG(ERROR) << "Failed to fsync " << name; + } if (ferror(fp)) { PLOG(ERROR) << "Error in " << name; } @@ -486,24 +490,18 @@ static void copy_logs() { // clear the recovery command and prepare to boot a (hopefully working) system, // copy our log file to cache as well (for the system to read). This function is // idempotent: call it as many times as you like. -static void -finish_recovery() { +static void finish_recovery() { // Save the locale to cache, so if recovery is next started up // without a --locale argument (eg, directly from the bootloader) // it will use the last-known locale. - if (locale != NULL) { - size_t len = strlen(locale); - __pmsg_write(LOCALE_FILE, locale, len); - if (has_cache) { - LOG(INFO) << "Saving locale \"" << locale << "\""; - FILE* fp = fopen_path(LOCALE_FILE, "w"); - if (fp != NULL) { - fwrite(locale, 1, len, fp); - fflush(fp); - fsync(fileno(fp)); - check_and_fclose(fp, LOCALE_FILE); - } + if (!locale.empty() && has_cache) { + LOG(INFO) << "Saving locale \"" << locale << "\""; + + FILE* fp = fopen_path(LOCALE_FILE, "w"); + if (!android::base::WriteStringToFd(locale, fileno(fp))) { + PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE; } + check_and_fclose(fp, LOCALE_FILE); } copy_logs(); @@ -1253,40 +1251,32 @@ print_property(const char *key, const char *name, void *cookie) { printf("%s=%s\n", key, name); } -static void -load_locale_from_cache() { - FILE* fp = fopen_path(LOCALE_FILE, "r"); - char buffer[80]; - if (fp != NULL) { - fgets(buffer, sizeof(buffer), fp); - int j = 0; - unsigned int i; - for (i = 0; i < sizeof(buffer) && buffer[i]; ++i) { - if (!isspace(buffer[i])) { - buffer[j++] = buffer[i]; - } - } - buffer[j] = 0; - locale = strdup(buffer); - check_and_fclose(fp, LOCALE_FILE); +static std::string load_locale_from_cache() { + if (ensure_path_mounted(LOCALE_FILE) != 0) { + LOG(ERROR) << "Can't mount " << LOCALE_FILE; + return ""; } -} -static RecoveryUI* gCurrentUI = NULL; + std::string content; + if (!android::base::ReadFileToString(LOCALE_FILE, &content)) { + PLOG(ERROR) << "Can't read " << LOCALE_FILE; + return ""; + } -void -ui_print(const char* format, ...) { - char buffer[256]; + return android::base::Trim(content); +} +void ui_print(const char* format, ...) { + std::string buffer; va_list ap; va_start(ap, format); - vsnprintf(buffer, sizeof(buffer), format, ap); + android::base::StringAppendV(&buffer, format, ap); va_end(ap); - if (gCurrentUI != NULL) { - gCurrentUI->Print("%s", buffer); + if (ui != nullptr) { + ui->Print("%s", buffer.c_str()); } else { - fputs(buffer, stdout); + fputs(buffer.c_str(), stdout); } } @@ -1295,8 +1285,8 @@ static constexpr char log_characters[] = "VDIWEF"; void UiLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag, const char* file, unsigned int line, const char* message) { - if (severity >= android::base::ERROR && gCurrentUI != NULL) { - gCurrentUI->Print("E:%s\n", message); + if (severity >= android::base::ERROR && ui != nullptr) { + ui->Print("E:%s\n", message); } else { fprintf(stdout, "%c:%s\n", log_characters[severity], message); } @@ -1392,7 +1382,7 @@ static void log_failure_code(ErrorCode code, const char *update_package) { }; std::string log_content = android::base::Join(log_buffer, "\n"); if (!android::base::WriteStringToFile(log_content, TEMPORARY_INSTALL_FILE)) { - PLOG(ERROR) << "failed to write " << TEMPORARY_INSTALL_FILE; + PLOG(ERROR) << "failed to write " << TEMPORARY_INSTALL_FILE; } // Also write the info into last_log. @@ -1495,18 +1485,24 @@ int main(int argc, char **argv) { } } - if (locale == nullptr && has_cache) { - load_locale_from_cache(); + if (locale.empty()) { + if (has_cache) { + locale = load_locale_from_cache(); + } + + if (locale.empty()) { + locale = DEFAULT_LOCALE; + } } - printf("locale is [%s]\n", locale); + + printf("locale is [%s]\n", locale.c_str()); printf("stage is [%s]\n", stage); printf("reason is [%s]\n", reason); Device* device = make_device(); ui = device->GetUI(); - gCurrentUI = ui; - ui->SetLocale(locale); + ui->SetLocale(locale.c_str()); ui->Init(); // Set background string to "installing security update" for security update, // otherwise set it to "installing system update". @@ -1521,7 +1517,7 @@ int main(int argc, char **argv) { if (show_text) ui->ShowText(true); struct selinux_opt seopts[] = { - { SELABEL_OPT_PATH, "/file_contexts" } + { SELABEL_OPT_PATH, "/file_contexts" } }; sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); @@ -1693,7 +1689,7 @@ int main(int argc, char **argv) { break; } while (true) { - pause(); + pause(); } // Should be unreachable. return EXIT_SUCCESS; @@ -17,7 +17,7 @@ #ifndef RECOVERY_ROOTS_H_ #define RECOVERY_ROOTS_H_ -#include "common.h" +typedef struct fstab_rec Volume; // Load and parse volume data from /etc/recovery.fstab. void load_volume_table(); diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index a859f11c1..337769e6b 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -16,7 +16,9 @@ #include <string> +#include <android-base/file.h> #include <android-base/properties.h> +#include <android-base/test_utils.h> #include <gtest/gtest.h> #include "edify/expr.h" @@ -97,3 +99,84 @@ TEST_F(UpdaterTest, sha1_check) { // sha1_check() expects at least one argument. expect(nullptr, "sha1_check()", kArgsParsingFailure); } + +TEST_F(UpdaterTest, file_getprop) { + // file_getprop() expects two arguments. + expect(nullptr, "file_getprop()", kArgsParsingFailure); + expect(nullptr, "file_getprop(\"arg1\")", kArgsParsingFailure); + expect(nullptr, "file_getprop(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure); + + // File doesn't exist. + expect(nullptr, "file_getprop(\"/doesntexist\", \"key1\")", kFileGetPropFailure); + + // Reject too large files (current limit = 65536). + TemporaryFile temp_file1; + std::string buffer(65540, '\0'); + ASSERT_TRUE(android::base::WriteStringToFile(buffer, temp_file1.path)); + + // Read some keys. + TemporaryFile temp_file2; + std::string content("ro.product.name=tardis\n" + "# comment\n\n\n" + "ro.product.model\n" + "ro.product.board = magic \n"); + ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file2.path)); + + std::string script1("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.name\")"); + expect("tardis", script1.c_str(), kNoCause); + + std::string script2("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.board\")"); + expect("magic", script2.c_str(), kNoCause); + + // No match. + std::string script3("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.wrong\")"); + expect("", script3.c_str(), kNoCause); + + std::string script4("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.name=\")"); + expect("", script4.c_str(), kNoCause); + + std::string script5("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.nam\")"); + expect("", script5.c_str(), kNoCause); + + std::string script6("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.model\")"); + expect("", script6.c_str(), kNoCause); +} + +TEST_F(UpdaterTest, delete) { + // Delete none. + expect("0", "delete()", kNoCause); + expect("0", "delete(\"/doesntexist\")", kNoCause); + expect("0", "delete(\"/doesntexist1\", \"/doesntexist2\")", kNoCause); + expect("0", "delete(\"/doesntexist1\", \"/doesntexist2\", \"/doesntexist3\")", kNoCause); + + // Delete one file. + TemporaryFile temp_file1; + ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file1.path)); + std::string script1("delete(\"" + std::string(temp_file1.path) + "\")"); + expect("1", script1.c_str(), kNoCause); + + // Delete two files. + TemporaryFile temp_file2; + ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file2.path)); + TemporaryFile temp_file3; + ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file3.path)); + std::string script2("delete(\"" + std::string(temp_file2.path) + "\", \"" + + std::string(temp_file3.path) + "\")"); + expect("2", script2.c_str(), kNoCause); + + // Delete already deleted files. + expect("0", script2.c_str(), kNoCause); + + // Delete one out of three. + TemporaryFile temp_file4; + ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file4.path)); + std::string script3("delete(\"/doesntexist1\", \"" + std::string(temp_file4.path) + + "\", \"/doesntexist2\")"); + expect("1", script3.c_str(), kNoCause); +} diff --git a/updater/install.cpp b/updater/install.cpp index efc96c454..25f6a9106 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -66,7 +66,7 @@ // Send over the buffer to recovery though the command pipe. static void uiPrint(State* state, const std::string& buffer) { - UpdaterInfo* ui = reinterpret_cast<UpdaterInfo*>(state->cookie); + UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie); // "line1\nline2\n" will be split into 3 tokens: "line1", "line2" and "". // So skip sending empty strings to UI. @@ -117,6 +117,7 @@ static int make_parents(char* name) { } // mount(fs_type, partition_type, location, mount_point) +// mount(fs_type, partition_type, location, mount_point, mount_options) // // fs_type="ext4" partition_type="EMMC" location=device Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { @@ -252,7 +253,6 @@ static int exec_cmd(const char* path, char* const argv[]) { return WEXITSTATUS(status); } - // format(fs_type, partition_type, location, fs_size, mount_point) // // fs_type="ext4" partition_type="EMMC" location=device fs_size=<bytes> mount_point=<location> @@ -301,14 +301,13 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { if (fs_type == "ext4") { int status = make_ext4fs(location.c_str(), size, mount_point.c_str(), sehandle); if (status != 0) { - printf("%s: make_ext4fs failed (%d) on %s", - name, status, location.c_str()); + printf("%s: make_ext4fs failed (%d) on %s", name, status, location.c_str()); return StringValue(""); } return StringValue(location); } else if (fs_type == "f2fs") { if (size < 0) { - printf("fs_size can't be negative for f2fs: %s", fs_size.c_str()); + printf("%s: fs_size can't be negative for f2fs: %s", name, fs_size.c_str()); return StringValue(""); } std::string num_sectors = std::to_string(size / 512); @@ -318,8 +317,7 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { num_sectors.c_str(), nullptr}; int status = exec_cmd(f2fs_path, (char* const*)f2fs_argv); if (status != 0) { - printf("%s: mkfs.f2fs failed (%d) on %s", - name, status, location.c_str()); + printf("%s: mkfs.f2fs failed (%d) on %s", name, status, location.c_str()); return StringValue(""); } return StringValue(location); @@ -365,8 +363,14 @@ Value* RenameFn(const char* name, State* state, int argc, Expr* argv[]) { return StringValue(dst_name); } +// delete([filename, ...]) +// Deletes all the filenames listed. Returns the number of files successfully deleted. +// +// delete_recursive([dirname, ...]) +// Recursively deletes dirnames and all their contents. Returns the number of directories +// successfully deleted. Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) { - std::vector<std::string> paths; + std::vector<std::string> paths(argc); for (int i = 0; i < argc; ++i) { if (!Evaluate(state, argv[i], &paths[i])) { return nullptr; @@ -382,7 +386,7 @@ Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) { } } - return StringValue(android::base::StringPrintf("%d", success)); + return StringValue(std::to_string(success)); } @@ -860,9 +864,13 @@ Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) { // file_getprop(file, key) // // interprets 'file' as a getprop-style file (key=value pairs, one -// per line. # comment lines,blank lines, lines without '=' ignored), +// per line. # comment lines, blank lines, lines without '=' ignored), // and returns the value for 'key' (or "" if it isn't defined). Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc); + } + std::vector<std::string> args; if (!ReadArgs(state, 2, argv, &args)) { return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); @@ -876,11 +884,10 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { filename.c_str(), strerror(errno)); } -#define MAX_FILE_GETPROP_SIZE 65536 - + constexpr off_t MAX_FILE_GETPROP_SIZE = 65536; if (st.st_size > MAX_FILE_GETPROP_SIZE) { - return ErrorAbort(state, kFileGetPropFailure, "%s too large for %s (max %d)", - filename.c_str(), name, MAX_FILE_GETPROP_SIZE); + return ErrorAbort(state, kFileGetPropFailure, "%s too large for %s (max %lld)", + filename.c_str(), name, static_cast<long long>(MAX_FILE_GETPROP_SIZE)); } std::string buffer(st.st_size, '\0'); @@ -913,7 +920,7 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { } // trim whitespace between key and '=' - std::string str = android::base::Trim(line.substr(0, equal_pos - 1)); + std::string str = android::base::Trim(line.substr(0, equal_pos)); // not the key we're looking for if (key != str) continue; diff --git a/verifier.cpp b/verifier.cpp index 82cdd3bc7..44098f70e 100644 --- a/verifier.cpp +++ b/verifier.cpp @@ -14,9 +14,11 @@ * limitations under the License. */ +#include "verifier.h" + #include <errno.h> -#include <malloc.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <algorithm> @@ -31,9 +33,6 @@ #include "common.h" #include "print_sha1.h" #include "ui.h" -#include "verifier.h" - -extern RecoveryUI* ui; static constexpr size_t MiB = 1024 * 1024; |