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.
|
||||
*/
|
||||
|
||||
#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) {
|
||||
|
|
Loading…
Reference in a new issue