summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--install.c170
1 files changed, 169 insertions, 1 deletions
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 <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
#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 <frac> <secs>
+ // fill up <frac> of the progress bar over <secs> seconds.
+ //
+ // firmware <"hboot"|"radio"> <filename>
+ // arrange to install the contents of <filename> 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) {