From b2ee9201be583b17ddbf0eaa69a37545f992b565 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 4 Jun 2009 10:24:53 -0700 Subject: [PATCH] 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(-) diff --git a/install.c b/install.c index e7db2a8..eff9312 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) {