From 8edb00c990e563e6f91b278a212f2edf877cf763 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 11 Jun 2009 17:21:44 -0700 Subject: [PATCH] edify extensions for OTA package installation, part 2 Adds more edify functions for OTAs: is_mounted getprop apply_patch apply_patch_check apply_patch_space write_raw_image write_firmware_image package_extract_file This allows us to install radios, hboots, boot images, and install incremental OTA packages. Fixes a couple of dumb bugs in edify itself: - we were doubling the size of the function table each time it was *not* full, rather than each time it was full - "no such function" errors weren't visible to the parser, so they didn't prevent execution of the script. --- edify/expr.c | 2 +- edify/main.c | 7 +- edify/parser.y | 10 +- install.c | 3 +- updater/Android.mk | 3 +- updater/install.c | 275 ++++++++++++++++++++++++++++++++++++++++++++- updater/updater.c | 7 +- 7 files changed, 291 insertions(+), 16 deletions(-) diff --git a/edify/expr.c b/edify/expr.c index 129fbd9..5470a2b 100644 --- a/edify/expr.c +++ b/edify/expr.c @@ -283,7 +283,7 @@ static int fn_size = 0; NamedFunction* fn_table = NULL; void RegisterFunction(const char* name, Function fn) { - if (fn_entries <= fn_size) { + if (fn_entries >= fn_size) { fn_size = fn_size*2 + 1; fn_table = realloc(fn_table, fn_size * sizeof(NamedFunction)); } diff --git a/edify/main.c b/edify/main.c index c959683..7da89e2 100644 --- a/edify/main.c +++ b/edify/main.c @@ -153,10 +153,11 @@ int main(int argc, char** argv) { buffer[size] = '\0'; Expr* root; + int error_count = 0; yy_scan_bytes(buffer, size); - int error = yyparse(&root); - printf("parse returned %d\n", error); - if (error == 0) { + int error = yyparse(&root, &error_count); + printf("parse returned %d; %d errors encountered\n", error, error_count); + if (error == 0 || error_count > 0) { char* result = Evaluate(NULL, root); if (result == NULL) { char* errmsg = GetError(); diff --git a/edify/parser.y b/edify/parser.y index 67a210f..cf163c0 100644 --- a/edify/parser.y +++ b/edify/parser.y @@ -25,8 +25,8 @@ extern int gLine; extern int gColumn; -void yyerror(Expr** root, const char* s); -int yyparse(Expr** root); +void yyerror(Expr** root, int* error_count, const char* s); +int yyparse(Expr** root, int* error_count); %} @@ -45,6 +45,7 @@ int yyparse(Expr** root); %type arglist %parse-param {Expr** root} +%parse-param {int* error_count} %error-verbose /* declarations in increasing order of precedence */ @@ -86,7 +87,7 @@ expr: STRING { if ($$->fn == NULL) { char buffer[256]; snprintf(buffer, sizeof(buffer), "unknown function \"%s\"", $1); - yyerror(root, buffer); + yyerror(root, error_count, buffer); YYERROR; } $$->name = $1; @@ -113,9 +114,10 @@ arglist: /* empty */ { %% -void yyerror(Expr** root, const char* s) { +void yyerror(Expr** root, int* error_count, const char* s) { if (strlen(s) == 0) { s = "syntax error"; } printf("line %d col %d: %s\n", gLine, gColumn, s); + ++*error_count; } diff --git a/install.c b/install.c index 0b5c04d..cca9400 100644 --- a/install.c +++ b/install.c @@ -124,7 +124,8 @@ handle_firmware_update(char* type, char* filename) { return INSTALL_ERROR; } - LOGI("type is [%s]\n", type); + LOGI("type is %s; size is %d; file is %s\n", + type, (int)st_data.st_size, filename); char* data = malloc(st_data.st_size); if (data == NULL) { diff --git a/updater/Android.mk b/updater/Android.mk index 2716d48..1159d7b 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -13,7 +13,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(updater_src_files) -LOCAL_STATIC_LIBRARIES := libedify libmtdutils libminzip libz +LOCAL_STATIC_LIBRARIES := libapplypatch libedify libmtdutils libminzip libz +LOCAL_STATIC_LIBRARIES += libmincrypt libbz LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. diff --git a/updater/install.c b/updater/install.c index 2336f61..2e965ce 100644 --- a/updater/install.c +++ b/updater/install.c @@ -24,6 +24,8 @@ #include #include +#include "cutils/misc.h" +#include "cutils/properties.h" #include "edify/expr.h" #include "minzip/DirUtil.h" #include "mtdutils/mounts.h" @@ -40,6 +42,7 @@ char* ErrorAbort(void* cookie, char* format, ...) { return NULL; } + // mount(type, location, mount_point) // // what: type="MTD" location="" to mount a yaffs2 filesystem @@ -104,6 +107,36 @@ done: return result; } + +// is_mounted(mount_point) +char* IsMountedFn(const char* name, void* cookie, int argc, Expr* argv[]) { + char* result = NULL; + if (argc != 1) { + return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc); + } + char* mount_point; + if (ReadArgs(cookie, argv, 1, &mount_point) < 0) { + return NULL; + } + if (strlen(mount_point) == 0) { + ErrorAbort(cookie, "mount_point argument to unmount() can't be empty"); + goto done; + } + + scan_mounted_volumes(); + const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point); + if (vol == NULL) { + result = strdup(""); + } else { + result = mount_point; + } + +done: + if (result != mount_point) free(mount_point); + return result; +} + + char* UnmountFn(const char* name, void* cookie, int argc, Expr* argv[]) { char* result = NULL; if (argc != 1) { @@ -132,6 +165,8 @@ done: if (result != mount_point) free(mount_point); return result; } + + // format(type, location) // // type="MTD" location=partition @@ -192,6 +227,7 @@ done: return result; } + char* DeleteFn(const char* name, void* cookie, int argc, Expr* argv[]) { char** paths = malloc(argc * sizeof(char*)); int i; @@ -222,6 +258,7 @@ char* DeleteFn(const char* name, void* cookie, int argc, Expr* argv[]) { return strdup(buffer); } + char* ShowProgressFn(const char* name, void* cookie, int argc, Expr* argv[]) { if (argc != 2) { return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); @@ -243,8 +280,9 @@ char* ShowProgressFn(const char* name, void* cookie, int argc, Expr* argv[]) { return strdup(""); } -// package_extract package_path destination_path -char* PackageExtractFn(const char* name, void* cookie, int argc, Expr* argv[]) { +// package_extract_dir(package_path, destination_path) +char* PackageExtractDirFn(const char* name, void* cookie, + int argc, Expr* argv[]) { if (argc != 2) { return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); } @@ -265,6 +303,42 @@ char* PackageExtractFn(const char* name, void* cookie, int argc, Expr* argv[]) { return strdup(success ? "t" : ""); } + +// package_extract_file(package_path, destination_path) +char* PackageExtractFileFn(const char* name, void* cookie, + int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(cookie, "%s() expects 2 args, got %d", name, argc); + } + char* zip_path; + char* dest_path; + if (ReadArgs(cookie, argv, 2, &zip_path, &dest_path) < 0) return NULL; + + bool success = false; + + ZipArchive* za = ((UpdaterInfo*)cookie)->package_zip; + const ZipEntry* entry = mzFindZipEntry(za, zip_path); + if (entry == NULL) { + fprintf(stderr, "%s: no %s in package\n", name, zip_path); + goto done; + } + + FILE* f = fopen(dest_path, "wb"); + if (f == NULL) { + fprintf(stderr, "%s: can't open %s for write: %s\n", + name, dest_path, strerror(errno)); + goto done; + } + success = mzExtractZipEntryToFile(za, entry, fileno(f)); + fclose(f); + + done: + free(zip_path); + free(dest_path); + return strdup(success ? "t" : ""); +} + + // symlink target src1 src2 ... char* SymlinkFn(const char* name, void* cookie, int argc, Expr* argv[]) { if (argc == 0) { @@ -289,6 +363,7 @@ char* SymlinkFn(const char* name, void* cookie, int argc, Expr* argv[]) { return strdup(""); } + char* SetPermFn(const char* name, void* cookie, int argc, Expr* argv[]) { char* result = NULL; bool recursive = (strcmp(name, "set_perm_recursive") == 0); @@ -356,15 +431,209 @@ done: return result; } + +char* GetPropFn(const char* name, void* cookie, int argc, Expr* argv[]) { + if (argc != 1) { + return ErrorAbort(cookie, "%s() expects 1 arg, got %d", name, argc); + } + char* key; + key = Evaluate(cookie, argv[0]); + if (key == NULL) return NULL; + + char value[PROPERTY_VALUE_MAX]; + property_get(key, value, ""); + free(key); + + return strdup(value); +} + + +static bool write_raw_image_cb(const unsigned char* data, + int data_len, void* ctx) { + int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len); + if (r == data_len) return true; + fprintf(stderr, "%s\n", strerror(errno)); + return false; +} + +// write_raw_image(file, partition) +char* WriteRawImageFn(const char* name, void* cookie, int argc, Expr* argv[]) { + char* result = NULL; + + char* partition; + char* filename; + if (ReadArgs(cookie, argv, 2, &filename, &partition) < 0) { + return NULL; + } + + if (strlen(partition) == 0) { + ErrorAbort(cookie, "partition argument to %s can't be empty", name); + goto done; + } + if (strlen(filename) == 0) { + ErrorAbort(cookie, "file argument to %s can't be empty", name); + goto done; + } + + mtd_scan_partitions(); + const MtdPartition* mtd = mtd_find_partition_by_name(partition); + if (mtd == NULL) { + fprintf(stderr, "%s: no mtd partition named \"%s\"\n", name, partition); + result = strdup(""); + goto done; + } + + MtdWriteContext* ctx = mtd_write_partition(mtd); + if (ctx == NULL) { + fprintf(stderr, "%s: can't write mtd partition \"%s\"\n", + name, partition); + result = strdup(""); + goto done; + } + + bool success; + + FILE* f = fopen(filename, "rb"); + if (f == NULL) { + fprintf(stderr, "%s: can't open %s: %s\n", + name, filename, strerror(errno)); + result = strdup(""); + goto done; + } + + success = true; + char* buffer = malloc(BUFSIZ); + int read; + while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) { + int wrote = mtd_write_data(ctx, buffer, read); + success = success && (wrote == read); + if (!success) { + fprintf(stderr, "mtd_write_data to %s failed: %s\n", + partition, strerror(errno)); + } + } + free(buffer); + fclose(f); + + printf("%s %s partition from %s\n", + success ? "wrote" : "failed to write", partition, filename); + + result = success ? partition : strdup(""); + +done: + if (result != partition) free(partition); + free(filename); + return result; +} + +// write_firmware_image(file, partition) +// +// partition is "radio" or "hboot" +// file is not used until after updater exits +// +// TODO: this should live in some HTC-specific library +char* WriteFirmwareImageFn(const char* name, void* cookie, + int argc, Expr* argv[]) { + char* result = NULL; + + char* partition; + char* filename; + if (ReadArgs(cookie, argv, 2, &filename, &partition) < 0) { + return NULL; + } + + if (strlen(partition) == 0) { + ErrorAbort(cookie, "partition argument to %s can't be empty", name); + goto done; + } + if (strlen(filename) == 0) { + ErrorAbort(cookie, "file argument to %s can't be empty", name); + goto done; + } + + FILE* cmd = ((UpdaterInfo*)cookie)->cmd_pipe; + fprintf(cmd, "firmware %s %s\n", partition, filename); + + printf("will write %s firmware from %s\n", partition, filename); + result = partition; + +done: + if (result != partition) free(partition); + free(filename); + return result; +} + + +extern int applypatch(int argc, char** argv); + +// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1:patch, ...) +// apply_patch_check(file, sha1, ...) +// apply_patch_space(bytes) +char* ApplyPatchFn(const char* name, void* cookie, int argc, Expr* argv[]) { + printf("in applypatchfn (%s)\n", name); + + char* prepend = NULL; + if (strstr(name, "check") != NULL) { + prepend = "-c"; + } else if (strstr(name, "space") != NULL) { + prepend = "-s"; + } + + char** args = ReadVarArgs(cookie, argc, argv); + if (args == NULL) return NULL; + + // insert the "program name" argv[0] and a copy of the "prepend" + // string (if any) at the start of the args. + + int extra = 1 + (prepend != NULL ? 1 : 0); + char** temp = malloc((argc+extra) * sizeof(char*)); + memcpy(temp+extra, args, argc * sizeof(char*)); + temp[0] = strdup("updater"); + if (prepend) { + temp[1] = strdup(prepend); + } + free(args); + args = temp; + argc += extra; + + printf("calling applypatch\n"); + fflush(stdout); + int result = applypatch(argc, args); + printf("applypatch returned %d\n", result); + + int i; + for (i = 0; i < argc; ++i) { + free(args[i]); + } + free(args); + + switch (result) { + case 0: return strdup("t"); + case 1: return strdup(""); + default: return ErrorAbort(cookie, "applypatch couldn't parse args"); + } +} + + void RegisterInstallFunctions() { RegisterFunction("mount", MountFn); + RegisterFunction("is_mounted", IsMountedFn); RegisterFunction("unmount", UnmountFn); RegisterFunction("format", FormatFn); RegisterFunction("show_progress", ShowProgressFn); RegisterFunction("delete", DeleteFn); RegisterFunction("delete_recursive", DeleteFn); - RegisterFunction("package_extract", PackageExtractFn); + RegisterFunction("package_extract_dir", PackageExtractDirFn); + RegisterFunction("package_extract_file", PackageExtractFileFn); RegisterFunction("symlink", SymlinkFn); RegisterFunction("set_perm", SetPermFn); RegisterFunction("set_perm_recursive", SetPermFn); + + RegisterFunction("getprop", GetPropFn); + RegisterFunction("write_raw_image", WriteRawImageFn); + RegisterFunction("write_firmware_image", WriteFirmwareImageFn); + + RegisterFunction("apply_patch", ApplyPatchFn); + RegisterFunction("apply_patch_check", ApplyPatchFn); + RegisterFunction("apply_patch_space", ApplyPatchFn); } diff --git a/updater/updater.c b/updater/updater.c index aa03803..0977625 100644 --- a/updater/updater.c +++ b/updater/updater.c @@ -80,10 +80,11 @@ int main(int argc, char** argv) { // Parse the script. Expr* root; + int error_count = 0; yy_scan_string(script); - int error = yyparse(&root); - if (error != 0) { - fprintf(stderr, "%d parse errors\n", error); + int error = yyparse(&root, &error_count); + if (error != 0 || error_count > 0) { + fprintf(stderr, "%d parse errors\n", error_count); return 6; }