summaryrefslogtreecommitdiffstats
path: root/install.c
diff options
context:
space:
mode:
authorJean-Baptiste Queru <jbq@google.com>2009-07-26 02:48:00 +0200
committerJean-Baptiste Queru <jbq@google.com>2009-07-26 02:48:00 +0200
commit7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7 (patch)
tree88c0fc6a69cade6f9d4437666c6420e0f757e278 /install.c
parentMerge commit 'korg/cupcake' (diff)
parentskip over all-zero blocks when reading MTD partition (diff)
downloadandroid_bootable_recovery-7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7.tar
android_bootable_recovery-7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7.tar.gz
android_bootable_recovery-7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7.tar.bz2
android_bootable_recovery-7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7.tar.lz
android_bootable_recovery-7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7.tar.xz
android_bootable_recovery-7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7.tar.zst
android_bootable_recovery-7bd5c660752ddd1b4ff6127b316fd6d8fb1005c7.zip
Diffstat (limited to 'install.c')
-rw-r--r--install.c313
1 files changed, 303 insertions, 10 deletions
diff --git a/install.c b/install.c
index 069112080..ab19478cf 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,13 +33,11 @@
#include "mtdutils/mtdutils.h"
#include "roots.h"
#include "verifier.h"
-
-/* List of public keys */
-static const RSAPublicKey keys[] = {
-#include "keys.inc"
-};
+#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 *
find_update_script(ZipArchive *zip)
@@ -99,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,12 +110,211 @@ 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;
}
+// The update binary ask us to install a firmware file on reboot. Set
+// that up. Takes ownership of type and filename.
static int
-handle_update_package(const char *path, ZipArchive *zip)
+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, data_size, filename);
+
+ char* data = malloc(data_size);
+ if (data == NULL) {
+ LOGI("Can't allocate %d bytes for firmware data\n", data_size);
+ 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);
+ }
+
+ if (remember_firmware_update(type, data, data_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
+ //
+ // - 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 the next <frac> part of of the progress bar
+ // over <secs> seconds. If <secs> is zero, use
+ // set_progress commands to manually control the
+ // progress of this segment of the bar
+ //
+ // set_progress <frac>
+ // <frac> 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"> <filename>
+ // arrange to install the contents of <filename> in the
+ // given partition on reboot. (API v2: <filename> may
+ // start with "PACKAGE:" to indicate taking a file from
+ // the OTA package.)
+ //
+ // ui_print <string>
+ // display <string> on the screen.
+ //
+ // - the name of the package zip file.
+ //
+
+ char** args = malloc(sizeof(char*) * 5);
+ args[0] = binary;
+ args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mk
+ 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, "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");
+
+ 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 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);
+ }
+ }
+ fclose(from_child);
+
+ int status;
+ waitpid(pid, &status, 0);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ LOGE("Error in %s\n(Status %d)\n", path, WEXITSTATUS(status));
+ return INSTALL_ERROR;
+ }
+
+ if (firmware_type != NULL) {
+ return handle_firmware_update(firmware_type, firmware_filename, zip);
+ } else {
+ return INSTALL_SUCCESS;
+ }
+}
+
+static int
+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 +322,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;
}
@@ -130,6 +330,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) {
@@ -147,6 +357,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 +453,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 +472,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;
}