From d1b19b9c98ac97db5c933d72dac5dca054a28353 Mon Sep 17 00:00:00 2001 From: Doug Zongker <> Date: Wed, 1 Apr 2009 15:48:46 -0700 Subject: AI 144130: Don't build OTA package keys into the recovery binary; read them from an external file in the recovery image. Use the test-keys for all builds. Automated import of CL 144130 --- install.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 8 deletions(-) (limited to 'install.c') diff --git a/install.c b/install.c index 069112080..4dcfe7536 100644 --- a/install.c +++ b/install.c @@ -31,12 +31,8 @@ #include "roots.h" #include "verifier.h" -/* List of public keys */ -static const RSAPublicKey keys[] = { -#include "keys.inc" -}; - #define ASSUMED_UPDATE_SCRIPT_NAME "META-INF/com/google/android/update-script" +#define PUBLIC_KEYS_FILE "/res/keys" static const ZipEntry * find_update_script(ZipArchive *zip) @@ -114,7 +110,8 @@ handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) } static int -handle_update_package(const char *path, ZipArchive *zip) +handle_update_package(const char *path, ZipArchive *zip, + const RSAPublicKey *keys, int numKeys) { // Give verification half the progress bar... ui_print("Verifying update package...\n"); @@ -122,7 +119,7 @@ handle_update_package(const char *path, ZipArchive *zip) VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME); - if (!verify_jar_signature(zip, keys, sizeof(keys) / sizeof(keys[0]))) { + if (!verify_jar_signature(zip, keys, numKeys)) { LOGE("Verification failed\n"); return INSTALL_CORRUPT; } @@ -147,6 +144,80 @@ handle_update_package(const char *path, ZipArchive *zip) return ret; } +// Reads a file containing one or more public keys as produced by +// DumpPublicKey: this is an RSAPublicKey struct as it would appear +// as a C source literal, eg: +// +// "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}" +// +// (Note that the braces and commas in this example are actual +// characters the parser expects to find in the file; the ellipses +// indicate more numbers omitted from this example.) +// +// The file may contain multiple keys in this format, separated by +// commas. The last key must not be followed by a comma. +// +// Returns NULL if the file failed to parse, or if it contain zero keys. +static RSAPublicKey* +load_keys(const char* filename, int* numKeys) { + RSAPublicKey* out = NULL; + *numKeys = 0; + + FILE* f = fopen(filename, "r"); + if (f == NULL) { + LOGE("opening %s: %s\n", filename, strerror(errno)); + goto exit; + } + + int i; + bool done = false; + while (!done) { + ++*numKeys; + out = realloc(out, *numKeys * sizeof(RSAPublicKey)); + RSAPublicKey* key = out + (*numKeys - 1); + if (fscanf(f, " { %i , %i , { %i", + &(key->len), &(key->n0inv), &(key->n[0])) != 3) { + goto exit; + } + if (key->len != RSANUMWORDS) { + LOGE("key length (%d) does not match expected size\n", key->len); + goto exit; + } + for (i = 1; i < key->len; ++i) { + if (fscanf(f, " , %i", &(key->n[i])) != 1) goto exit; + } + if (fscanf(f, " } , { %i", &(key->rr[0])) != 1) goto exit; + for (i = 1; i < key->len; ++i) { + if (fscanf(f, " , %i", &(key->rr[i])) != 1) goto exit; + } + fscanf(f, " } } "); + + // if the line ends in a comma, this file has more keys. + switch (fgetc(f)) { + case ',': + // more keys to come. + break; + + case EOF: + done = true; + break; + + default: + LOGE("unexpected character between keys\n"); + goto exit; + } + } + + fclose(f); + return out; + +exit: + if (f) fclose(f); + free(out); + *numKeys = 0; + return NULL; +} + int install_package(const char *root_path) { @@ -169,6 +240,14 @@ install_package(const char *root_path) ui_print("Opening update package...\n"); LOGI("Update file path: %s\n", path); + int numKeys; + RSAPublicKey* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys); + if (loadedKeys == NULL) { + LOGE("Failed to load keys\n"); + return INSTALL_CORRUPT; + } + LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE); + /* Try to open the package. */ ZipArchive zip; @@ -180,7 +259,8 @@ install_package(const char *root_path) /* Verify and install the contents of the package. */ - int status = handle_update_package(path, &zip); + int status = handle_update_package(path, &zip, loadedKeys, numKeys); mzCloseZipArchive(&zip); + free(loadedKeys); return status; } -- cgit v1.2.3 From 07e1dca7068284c4f3013550335029eb72b39b82 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 28 May 2009 19:02:45 -0700 Subject: don't say "install complete" when it really isn't Change the recovery UI so that when there is a hboot or radio update pending (which the user most do a home+back reboot to actually install), the UI tells them so, instead of saying "Install from sdcard complete." --- install.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'install.c') diff --git a/install.c b/install.c index 4dcfe7536..e7db2a8f2 100644 --- a/install.c +++ b/install.c @@ -105,7 +105,7 @@ handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) return INSTALL_ERROR; } - ui_print("Installation complete.\n"); + LOGI("Installation complete.\n"); return INSTALL_SUCCESS; } -- cgit v1.2.3 From b2ee9201be583b17ddbf0eaa69a37545f992b565 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 4 Jun 2009 10:24:53 -0700 Subject: allow OTA package to provide binary instead of script Allow installation of OTA packages which do not contain an update-script, but instead contain an update-binary. --- install.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 169 insertions(+), 1 deletion(-) (limited to 'install.c') diff --git a/install.c b/install.c index e7db2a8f2..eff9312b4 100644 --- a/install.c +++ b/install.c @@ -14,10 +14,13 @@ * limitations under the License. */ +#include #include #include #include #include +#include +#include #include "amend/amend.h" #include "common.h" @@ -30,8 +33,10 @@ #include "mtdutils/mtdutils.h" #include "roots.h" #include "verifier.h" +#include "firmware.h" #define ASSUMED_UPDATE_SCRIPT_NAME "META-INF/com/google/android/update-script" +#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" #define PUBLIC_KEYS_FILE "/res/keys" static const ZipEntry * @@ -95,7 +100,7 @@ handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) int ret = execCommandList((ExecContext *)1, commands); if (ret != 0) { int num = ret; - char *line, *next = script_data; + char *line = NULL, *next = script_data; while (next != NULL && ret-- > 0) { line = next; next = memchr(line, '\n', script_data + script_len - line); @@ -109,6 +114,159 @@ handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) return INSTALL_SUCCESS; } +// The update binary ask us to install a firmware file on reboot. Set +// that up. Takes ownership of type and filename. +static int +handle_firmware_update(char* type, char* filename) { + struct stat st_data; + if (stat(filename, &st_data) < 0) { + LOGE("Error stat'ing %s: %s\n", filename, strerror(errno)); + return INSTALL_ERROR; + } + + LOGI("type is [%s]\n", type); + + char* data = malloc(st_data.st_size); + if (data == NULL) { + LOGE("Can't allocate %d bytes for firmware data\n", st_data.st_size); + return INSTALL_ERROR; + } + + FILE* f = fopen(filename, "rb"); + if (f == NULL) { + LOGE("Failed to open %s: %s\n", filename, strerror(errno)); + return INSTALL_ERROR; + } + if (fread(data, 1, st_data.st_size, f) != st_data.st_size) { + LOGE("Failed to read firmware data: %s\n", strerror(errno)); + return INSTALL_ERROR; + } + fclose(f); + + if (remember_firmware_update(type, data, st_data.st_size)) { + LOGE("Can't store %s image\n", type); + free(data); + return INSTALL_ERROR; + } + free(filename); + + return INSTALL_SUCCESS; +} + +// If the package contains an update binary, extract it and run it. +static int +try_update_binary(const char *path, ZipArchive *zip) { + const ZipEntry* binary_entry = + mzFindZipEntry(zip, ASSUMED_UPDATE_BINARY_NAME); + if (binary_entry == NULL) { + return INSTALL_CORRUPT; + } + + char* binary = "/tmp/update_binary"; + unlink(binary); + int fd = creat(binary, 0755); + if (fd < 0) { + LOGE("Can't make %s\n", binary); + return 1; + } + bool ok = mzExtractZipEntryToFile(zip, binary_entry, fd); + close(fd); + + if (!ok) { + LOGE("Can't copy %s\n", ASSUMED_UPDATE_BINARY_NAME); + return 1; + } + + int pipefd[2]; + pipe(pipefd); + + // When executing the update binary contained in the package, the + // arguments passed are: + // + // - the version number for this interface (currently 1) + // + // - an fd to which the program can write in order to update the + // progress bar. The program can write single-line commands: + // + // progress + // fill up of the progress bar over seconds. + // + // firmware <"hboot"|"radio"> + // arrange to install the contents of in the + // given partition on reboot. + // + // - the name of the package zip file. + // + + char** args = malloc(sizeof(char*) * 5); + args[0] = binary; + args[1] = "1"; + args[2] = malloc(10); + sprintf(args[2], "%d", pipefd[1]); + args[3] = (char*)path; + args[4] = NULL; + + pid_t pid = fork(); + if (pid == 0) { + close(pipefd[0]); + execv(binary, args); + fprintf(stderr, "E:Can't run %s (%s)\n", binary, strerror(errno)); + _exit(-1); + } + close(pipefd[1]); + + char* firmware_type = NULL; + char* firmware_filename = NULL; + + char buffer[81]; + FILE* from_child = fdopen(pipefd[0], "r"); + while (fgets(buffer, sizeof(buffer), from_child) != NULL) { + LOGI("read: %s", buffer); + + char* command = strtok(buffer, " \n"); + if (command == NULL) { + continue; + } else if (strcmp(command, "progress") == 0) { + char* fraction_s = strtok(NULL, " \n"); + char* seconds_s = strtok(NULL, " \n"); + + float fraction = strtof(fraction_s, NULL); + int seconds = strtol(seconds_s, NULL, 10); + + ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), + seconds); + } else if (strcmp(command, "firmware") == 0) { + char* type = strtok(NULL, " \n"); + char* filename = strtok(NULL, " \n"); + + if (type != NULL && filename != NULL) { + if (firmware_type != NULL) { + LOGE("ignoring attempt to do multiple firmware updates"); + } else { + firmware_type = strdup(type); + firmware_filename = strdup(filename); + } + } + } else { + LOGE("unknown command [%s]\n", command); + } + } + fclose(from_child); + + int status; + waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + LOGE("Error in %s\n(Status %d)\n", path, status); + return INSTALL_ERROR; + } + + if (firmware_type != NULL) { + return handle_firmware_update(firmware_type, firmware_filename); + } else { + return INSTALL_SUCCESS; + } +} + static int handle_update_package(const char *path, ZipArchive *zip, const RSAPublicKey *keys, int numKeys) @@ -127,6 +285,16 @@ handle_update_package(const char *path, ZipArchive *zip, // Update should take the rest of the progress bar. ui_print("Installing update...\n"); + int result = try_update_binary(path, zip); + if (result == INSTALL_SUCCESS || result == INSTALL_ERROR) { + register_package_root(NULL, NULL); // Unregister package root + return result; + } + + // if INSTALL_CORRUPT is returned, this package doesn't have an + // update binary. Fall back to the older mechanism of looking for + // an update script. + const ZipEntry *script_entry; script_entry = find_update_script(zip); if (script_entry == NULL) { -- cgit v1.2.3 From 9931f7f3c1288171319e9ff7d053ebaad07db720 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 10 Jun 2009 14:11:53 -0700 Subject: edify extensions for OTA package installation, part 1 Adds the following edify functions: mount unmount format show_progress delete delete_recursive package_extract symlink set_perm set_perm_recursive This set is enough to extract and install the system part of a (full) OTA package. Adds the updater binary that extracts an edify script from the OTA package and then executes it. Minor changes to the edify core (adds a sleep() builtin for debugging, adds "." to the set of characters that can appear in an unquoted string). --- install.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'install.c') diff --git a/install.c b/install.c index eff9312b4..0b5c04da9 100644 --- a/install.c +++ b/install.c @@ -256,7 +256,7 @@ try_update_binary(const char *path, ZipArchive *zip) { int status; waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - LOGE("Error in %s\n(Status %d)\n", path, status); + LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status)); return INSTALL_ERROR; } -- cgit v1.2.3 From 8edb00c990e563e6f91b278a212f2edf877cf763 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 11 Jun 2009 17:21:44 -0700 Subject: edify extensions for OTA package installation, part 2 Adds more edify functions for OTAs: is_mounted getprop apply_patch apply_patch_check apply_patch_space write_raw_image write_firmware_image package_extract_file This allows us to install radios, hboots, boot images, and install incremental OTA packages. Fixes a couple of dumb bugs in edify itself: - we were doubling the size of the function table each time it was *not* full, rather than each time it was full - "no such function" errors weren't visible to the parser, so they didn't prevent execution of the script. --- install.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'install.c') diff --git a/install.c b/install.c index 0b5c04da9..cca940021 100644 --- a/install.c +++ b/install.c @@ -124,7 +124,8 @@ handle_firmware_update(char* type, char* filename) { return INSTALL_ERROR; } - LOGI("type is [%s]\n", type); + LOGI("type is %s; size is %d; file is %s\n", + type, (int)st_data.st_size, filename); char* data = malloc(st_data.st_size); if (data == NULL) { -- cgit v1.2.3 From d9c9d10d9da76f067d3955bea71f7bb39e859fa5 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 12 Jun 2009 12:24:39 -0700 Subject: fixes to edify and updater script A few more changes to edify: - fix write_raw_image(); my last change neglected to close the write context, so the written image was corrupt. - each expression tracks the span of the source code from which it was compiled, so that assert()'s error message can include the source of the expression that failed. - the 'cookie' argument to each Function is replaced with a State object, which contains the cookie, the source script (for use with the above spans), and the current error message (replacing the global variables that were used for this purpose). - in the recovery image, a new command "ui_print" can be sent back through the command pipe to cause text to appear on the screen. Add a new ui_print() function to print things from scripts. Rename existing "print" function to "stdout". --- install.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'install.c') diff --git a/install.c b/install.c index cca940021..c2e1385b0 100644 --- a/install.c +++ b/install.c @@ -196,6 +196,9 @@ try_update_binary(const char *path, ZipArchive *zip) { // arrange to install the contents of in the // given partition on reboot. // + // ui_print + // display on the screen. + // // - the name of the package zip file. // @@ -248,6 +251,13 @@ try_update_binary(const char *path, ZipArchive *zip) { firmware_filename = strdup(filename); } } + } else if (strcmp(command, "ui_print") == 0) { + char* str = strtok(NULL, "\n"); + if (str) { + ui_print(str); + } else { + ui_print("\n"); + } } else { LOGE("unknown command [%s]\n", command); } -- cgit v1.2.3 From fb2e3af3f915c0e3f2b4b027ef26777267ad46dc Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 17 Jun 2009 17:29:40 -0700 Subject: let the "firmware" command take the file straight from the package To do a firmware-install-on-reboot, the update binary tells recovery what file to install before rebooting. Let this file be specified as "PACKAGE:" to indicate taking the file out of the OTA package, avoiding an extra copy to /tmp. Bump the API version number to reflect this change. --- install.c | 66 ++++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 22 deletions(-) (limited to 'install.c') diff --git a/install.c b/install.c index c2e1385b0..cbb35805c 100644 --- a/install.c +++ b/install.c @@ -117,34 +117,54 @@ handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry) // The update binary ask us to install a firmware file on reboot. Set // that up. Takes ownership of type and filename. static int -handle_firmware_update(char* type, char* filename) { - struct stat st_data; - if (stat(filename, &st_data) < 0) { - LOGE("Error stat'ing %s: %s\n", filename, strerror(errno)); - return INSTALL_ERROR; +handle_firmware_update(char* type, char* filename, ZipArchive* zip) { + unsigned int data_size; + const ZipEntry* entry = NULL; + + if (strncmp(filename, "PACKAGE:", 8) == 0) { + entry = mzFindZipEntry(zip, filename+8); + if (entry == NULL) { + LOGE("Failed to find \"%s\" in package", filename+8); + return INSTALL_ERROR; + } + data_size = entry->uncompLen; + } else { + struct stat st_data; + if (stat(filename, &st_data) < 0) { + LOGE("Error stat'ing %s: %s\n", filename, strerror(errno)); + return INSTALL_ERROR; + } + data_size = st_data.st_size; } LOGI("type is %s; size is %d; file is %s\n", - type, (int)st_data.st_size, filename); + type, data_size, filename); - char* data = malloc(st_data.st_size); + char* data = malloc(data_size); if (data == NULL) { - LOGE("Can't allocate %d bytes for firmware data\n", st_data.st_size); + LOGI("Can't allocate %d bytes for firmware data\n", data_size); return INSTALL_ERROR; } - FILE* f = fopen(filename, "rb"); - if (f == NULL) { - LOGE("Failed to open %s: %s\n", filename, strerror(errno)); - return INSTALL_ERROR; - } - if (fread(data, 1, st_data.st_size, f) != st_data.st_size) { - LOGE("Failed to read firmware data: %s\n", strerror(errno)); - return INSTALL_ERROR; + if (entry) { + if (mzReadZipEntry(zip, entry, data, data_size) == false) { + LOGE("Failed to read \"%s\" from package", filename+8); + return INSTALL_ERROR; + } + } else { + FILE* f = fopen(filename, "rb"); + if (f == NULL) { + LOGE("Failed to open %s: %s\n", filename, strerror(errno)); + return INSTALL_ERROR; + } + if (fread(data, 1, data_size, f) != data_size) { + LOGE("Failed to read firmware data: %s\n", strerror(errno)); + return INSTALL_ERROR; + } + fclose(f); } - fclose(f); - if (remember_firmware_update(type, data, st_data.st_size)) { + if (remember_firmware_update(type, data, data_size)) { LOGE("Can't store %s image\n", type); free(data); return INSTALL_ERROR; @@ -184,7 +204,7 @@ try_update_binary(const char *path, ZipArchive *zip) { // When executing the update binary contained in the package, the // arguments passed are: // - // - the version number for this interface (currently 1) + // - the version number for this interface // // - an fd to which the program can write in order to update the // progress bar. The program can write single-line commands: @@ -194,7 +214,9 @@ try_update_binary(const char *path, ZipArchive *zip) { // // firmware <"hboot"|"radio"> // arrange to install the contents of in the - // given partition on reboot. + // given partition on reboot. (API v2: may + // start with "PACKAGE:" to indicate taking a file from + // the OTA package.) // // ui_print // display on the screen. @@ -204,7 +226,7 @@ try_update_binary(const char *path, ZipArchive *zip) { char** args = malloc(sizeof(char*) * 5); args[0] = binary; - args[1] = "1"; + args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk args[2] = malloc(10); sprintf(args[2], "%d", pipefd[1]); args[3] = (char*)path; @@ -272,7 +294,7 @@ try_update_binary(const char *path, ZipArchive *zip) { } if (firmware_type != NULL) { - return handle_firmware_update(firmware_type, firmware_filename); + return handle_firmware_update(firmware_type, firmware_filename, zip); } else { return INSTALL_SUCCESS; } -- cgit v1.2.3 From fbf3c10e45c20f8fe6bd1ac49ffe220035b9c454 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 24 Jun 2009 09:36:20 -0700 Subject: improve updater progress bar Let recovery accept set_progress commands to control progress over the 'current segment' of the bar. Add a set_progress() builtin to the updater binary. --- install.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'install.c') diff --git a/install.c b/install.c index cbb35805c..ab19478cf 100644 --- a/install.c +++ b/install.c @@ -210,7 +210,15 @@ try_update_binary(const char *path, ZipArchive *zip) { // progress bar. The program can write single-line commands: // // progress - // fill up of the progress bar over seconds. + // fill up the next part of of the progress bar + // over seconds. If is zero, use + // set_progress commands to manually control the + // progress of this segment of the bar + // + // set_progress + // should be between 0.0 and 1.0; sets the + // progress bar within the segment defined by the most + // recent progress command. // // firmware <"hboot"|"radio"> // arrange to install the contents of in the @@ -261,6 +269,10 @@ try_update_binary(const char *path, ZipArchive *zip) { ui_show_progress(fraction * (1-VERIFICATION_PROGRESS_FRACTION), seconds); + } else if (strcmp(command, "set_progress") == 0) { + char* fraction_s = strtok(NULL, " \n"); + float fraction = strtof(fraction_s, NULL); + ui_set_progress(fraction); } else if (strcmp(command, "firmware") == 0) { char* type = strtok(NULL, " \n"); char* filename = strtok(NULL, " \n"); -- cgit v1.2.3