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.
This commit is contained in:
parent
f28c916e73
commit
b2ee9201be
1 changed files with 169 additions and 1 deletions
170
install.c
170
install.c
|
@ -14,10 +14,13 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "amend/amend.h"
|
#include "amend/amend.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
@ -30,8 +33,10 @@
|
||||||
#include "mtdutils/mtdutils.h"
|
#include "mtdutils/mtdutils.h"
|
||||||
#include "roots.h"
|
#include "roots.h"
|
||||||
#include "verifier.h"
|
#include "verifier.h"
|
||||||
|
#include "firmware.h"
|
||||||
|
|
||||||
#define ASSUMED_UPDATE_SCRIPT_NAME "META-INF/com/google/android/update-script"
|
#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"
|
#define PUBLIC_KEYS_FILE "/res/keys"
|
||||||
|
|
||||||
static const ZipEntry *
|
static const ZipEntry *
|
||||||
|
@ -95,7 +100,7 @@ handle_update_script(ZipArchive *zip, const ZipEntry *update_script_entry)
|
||||||
int ret = execCommandList((ExecContext *)1, commands);
|
int ret = execCommandList((ExecContext *)1, commands);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
int num = ret;
|
int num = ret;
|
||||||
char *line, *next = script_data;
|
char *line = NULL, *next = script_data;
|
||||||
while (next != NULL && ret-- > 0) {
|
while (next != NULL && ret-- > 0) {
|
||||||
line = next;
|
line = next;
|
||||||
next = memchr(line, '\n', script_data + script_len - line);
|
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;
|
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
|
static int
|
||||||
handle_update_package(const char *path, ZipArchive *zip,
|
handle_update_package(const char *path, ZipArchive *zip,
|
||||||
const RSAPublicKey *keys, int numKeys)
|
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.
|
// Update should take the rest of the progress bar.
|
||||||
ui_print("Installing update...\n");
|
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;
|
const ZipEntry *script_entry;
|
||||||
script_entry = find_update_script(zip);
|
script_entry = find_update_script(zip);
|
||||||
if (script_entry == NULL) {
|
if (script_entry == NULL) {
|
||||||
|
|
Loading…
Reference in a new issue