From 46ab1b6138bf911120280f1095ff9479dceee3df Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Wed, 11 Sep 2013 11:37:10 -0700 Subject: updater: Delete dead code set_perm and set_perm_recursive are no longer used. Delete. (cherry picked from commit 08ef9a957027183dcf55e432441e8fb0d5299aba) Change-Id: I1bcc90ae19af9df4f0705496c5876987159f75ac --- updater/install.c | 87 ------------------------------------------------------- 1 file changed, 87 deletions(-) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index 0a859452a..061aa7b5b 100644 --- a/updater/install.c +++ b/updater/install.c @@ -524,88 +524,6 @@ Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { return StringValue(strdup("")); } - -Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { - char* result = NULL; - bool recursive = (strcmp(name, "set_perm_recursive") == 0); - - int min_args = 4 + (recursive ? 1 : 0); - if (argc < min_args) { - return ErrorAbort(state, "%s() expects %d+ args, got %d", - name, min_args, argc); - } - - char** args = ReadVarArgs(state, argc, argv); - if (args == NULL) return NULL; - - char* end; - int i; - int bad = 0; - - int uid = strtoul(args[0], &end, 0); - if (*end != '\0' || args[0][0] == 0) { - ErrorAbort(state, "%s: \"%s\" not a valid uid", name, args[0]); - goto done; - } - - int gid = strtoul(args[1], &end, 0); - if (*end != '\0' || args[1][0] == 0) { - ErrorAbort(state, "%s: \"%s\" not a valid gid", name, args[1]); - goto done; - } - - if (recursive) { - int dir_mode = strtoul(args[2], &end, 0); - if (*end != '\0' || args[2][0] == 0) { - ErrorAbort(state, "%s: \"%s\" not a valid dirmode", name, args[2]); - goto done; - } - - int file_mode = strtoul(args[3], &end, 0); - if (*end != '\0' || args[3][0] == 0) { - ErrorAbort(state, "%s: \"%s\" not a valid filemode", - name, args[3]); - goto done; - } - - for (i = 4; i < argc; ++i) { - dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode); - } - } else { - int mode = strtoul(args[2], &end, 0); - if (*end != '\0' || args[2][0] == 0) { - ErrorAbort(state, "%s: \"%s\" not a valid mode", name, args[2]); - goto done; - } - - for (i = 3; i < argc; ++i) { - if (chown(args[i], uid, gid) < 0) { - printf("%s: chown of %s to %d %d failed: %s\n", - name, args[i], uid, gid, strerror(errno)); - ++bad; - } - if (chmod(args[i], mode) < 0) { - printf("%s: chmod of %s to %o failed: %s\n", - name, args[i], mode, strerror(errno)); - ++bad; - } - } - } - result = strdup(""); - -done: - for (i = 0; i < argc; ++i) { - free(args[i]); - } - free(args); - - if (bad) { - free(result); - return ErrorAbort(state, "%s: some changes failed", name); - } - return StringValue(result); -} - struct perm_parsed_args { bool has_uid; uid_t uid; @@ -1398,11 +1316,6 @@ void RegisterInstallFunctions() { RegisterFunction("package_extract_file", PackageExtractFileFn); RegisterFunction("symlink", SymlinkFn); - // Maybe, at some future point, we can delete these functions? They have been - // replaced by perm_set and perm_set_recursive. - RegisterFunction("set_perm", SetPermFn); - RegisterFunction("set_perm_recursive", SetPermFn); - // Usage: // set_metadata("filename", "key1", "value1", "key2", "value2", ...) // Example: -- cgit v1.2.3 From ce7ca7165bb263c91ca7616a21457032c645e23d Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Wed, 6 Nov 2013 17:42:20 -0800 Subject: Enable incremental builder to find files that moved, and try to process them via patch + rename, instead of delete + add. b/11437930 Change-Id: I984349fbc9a8dac4379e00c0d66fc7d22c4eb834 --- updater/install.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index 0a859452a..9fb1b63d7 100644 --- a/updater/install.c +++ b/updater/install.c @@ -285,6 +285,40 @@ done: return StringValue(result); } +Value* RenameFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + + char* src_name; + char* dst_name; + + if (ReadArgs(state, argv, 2, &src_name, &dst_name) < 0) { + return NULL; + } + if (strlen(src_name) == 0) { + ErrorAbort(state, "src_name argument to %s() can't be empty", name); + goto done; + } + if (strlen(dst_name) == 0) { + ErrorAbort(state, "dst_name argument to %s() can't be empty", + name); + goto done; + } + + if (rename(src_name, dst_name) != 0) { + ErrorAbort(state, "Rename of %s() to %s() failed, error %s()", + src_name, dst_name, strerror(errno)); + } else { + result = dst_name; + } + +done: + free(src_name); + if (result != dst_name) free(dst_name); + return StringValue(result); +} Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) { char** paths = malloc(argc * sizeof(char*)); @@ -1425,6 +1459,7 @@ void RegisterInstallFunctions() { RegisterFunction("read_file", ReadFileFn); RegisterFunction("sha1_check", Sha1CheckFn); + RegisterFunction("rename", RenameFn); RegisterFunction("wipe_cache", WipeCacheFn); -- cgit v1.2.3 From c87bab101893e8322b49d7c8600e3367b20ab50a Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 25 Nov 2013 13:53:25 -0800 Subject: add the functions for multi-stage packages to updater In order to support multi-stage recovery packages, we add the set_stage() and get_stage() functions, which store a short string somewhere it can be accessed across invocations of recovery. We also add reboot_now() which updater can invoke to immediately reboot the device, without doing normal recovery cleanup. (It can also choose whether to boot off the boot or recovery partition.) If the stage string is of the form "#/#", recovery's UI will be augmented with a simple indicator of what stage you're in, so it doesn't look like a reboot loop. Change-Id: I62f7ff0bc802b549c9bcf3cc154a6bad99f94603 --- updater/install.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index 9fb1b63d7..9f299f6cc 100644 --- a/updater/install.c +++ b/updater/install.c @@ -34,6 +34,9 @@ #include #include +#include "bootloader.h" +#include "applypatch/applypatch.h" +#include "cutils/android_reboot.h" #include "cutils/misc.h" #include "cutils/properties.h" #include "edify/expr.h" @@ -42,7 +45,6 @@ #include "mtdutils/mounts.h" #include "mtdutils/mtdutils.h" #include "updater.h" -#include "applypatch/applypatch.h" #ifdef USE_EXT4 #include "make_ext4fs.h" @@ -1419,6 +1421,105 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) { return v; } +// Immediately reboot the device. Recovery is not finished normally, +// so if you reboot into recovery it will re-start applying the +// current package (because nothing has cleared the copy of the +// arguments stored in the BCB). +// +// The argument is the partition name passed to the android reboot +// property. It can be "recovery" to boot from the recovery +// partition, or "" (empty string) to boot from the regular boot +// partition. +Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + + char* filename; + char* property; + if (ReadArgs(state, argv, 2, &filename, &property) < 0) return NULL; + + char buffer[80]; + + // zero out the 'command' field of the bootloader message. + memset(buffer, 0, sizeof(((struct bootloader_message*)0)->command)); + FILE* f = fopen(filename, "r+b"); + fseek(f, offsetof(struct bootloader_message, command), SEEK_SET); + fwrite(buffer, sizeof(((struct bootloader_message*)0)->command), 1, f); + fclose(f); + free(filename); + + strcpy(buffer, "reboot,"); + if (property != NULL) { + strncat(buffer, property, sizeof(buffer)-10); + } + + property_set(ANDROID_RB_PROPERTY, buffer); + + sleep(5); + free(property); + ErrorAbort(state, "%s() failed to reboot", name); + return NULL; +} + +// Store a string value somewhere that future invocations of recovery +// can access it. This value is called the "stage" and can be used to +// drive packages that need to do reboots in the middle of +// installation and keep track of where they are in the multi-stage +// install. +// +// The first argument is the block device for the misc partition +// ("/misc" in the fstab), which is where this value is stored. The +// second argument is the string to store; it should not exceed 31 +// bytes. +Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + + char* filename; + char* stagestr; + if (ReadArgs(state, argv, 2, &filename, &stagestr) < 0) return NULL; + + // Store this value in the misc partition, immediately after the + // bootloader message that the main recovery uses to save its + // arguments in case of the device restarting midway through + // package installation. + FILE* f = fopen(filename, "r+b"); + fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET); + int to_write = strlen(stagestr)+1; + int max_size = sizeof(((struct bootloader_message*)0)->stage); + if (to_write > max_size) { + to_write = max_size; + stagestr[max_size-1] = 0; + } + fwrite(stagestr, to_write, 1, f); + fclose(f); + + free(stagestr); + return StringValue(filename); +} + +// Return the value most recently saved with SetStageFn. The argument +// is the block device for the misc partition. +Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + } + + char* filename; + if (ReadArgs(state, argv, 1, &filename) < 0) return NULL; + + char buffer[sizeof(((struct bootloader_message*)0)->stage)]; + FILE* f = fopen(filename, "rb"); + fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET); + fread(buffer, sizeof(buffer), 1, f); + fclose(f); + buffer[sizeof(buffer)-1] = '\0'; + + return StringValue(strdup(buffer)); +} + void RegisterInstallFunctions() { RegisterFunction("mount", MountFn); RegisterFunction("is_mounted", IsMountedFn); @@ -1466,4 +1567,8 @@ void RegisterInstallFunctions() { RegisterFunction("ui_print", UIPrintFn); RegisterFunction("run_program", RunProgramFn); + + RegisterFunction("reboot_now", RebootNowFn); + RegisterFunction("get_stage", GetStageFn); + RegisterFunction("set_stage", SetStageFn); } -- cgit v1.2.3 From 6eed2247714967eb0ce913d7da782fa884a4c1bd Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Fri, 13 Dec 2013 17:13:11 -0800 Subject: Don't abort on read_file if the file is missing. Change-Id: I85726bf736203d602428114145c3b98692580656 --- updater/install.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index 9f299f6cc..b6852b0c7 100644 --- a/updater/install.c +++ b/updater/install.c @@ -1353,7 +1353,6 @@ Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) { } if (args[0]->size < 0) { - printf("%s(): no file contents received", name); return StringValue(strdup("")); } uint8_t digest[SHA_DIGEST_SIZE]; @@ -1406,12 +1405,11 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) { FileContents fc; if (LoadFileContents(filename, &fc, RETOUCH_DONT_MASK) != 0) { - ErrorAbort(state, "%s() loading \"%s\" failed: %s", - name, filename, strerror(errno)); free(filename); - free(v); + v->size = -1; + v->data = NULL; free(fc.data); - return NULL; + return v; } v->size = fc.size; -- cgit v1.2.3 From 99916f0496cfe37891d40f21a9a0e387620a8a60 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 13 Jan 2014 14:16:58 -0800 Subject: do verification and extraction on memory, not files Changes minzip and recovery's file signature verification to work on memory regions, rather than files. For packages which are regular files, install.cpp now mmap()s them into memory and then passes the mapped memory to the verifier and to the minzip library. Support for files which are raw block maps (which will be used when we have packages written to encrypted data partitions) is present but largely untested so far. Bug: 12188746 Change-Id: I12cc3e809834745a489dd9d4ceb558cbccdc3f71 --- updater/updater.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'updater') diff --git a/updater/updater.c b/updater/updater.c index c7009feac..4e1cc9c38 100644 --- a/updater/updater.c +++ b/updater/updater.c @@ -22,6 +22,7 @@ #include "updater.h" #include "install.h" #include "minzip/Zip.h" +#include "minzip/SysUtil.h" // Generated by the makefile, this function defines the // RegisterDeviceExtensions() function, which calls all the @@ -65,19 +66,24 @@ int main(int argc, char** argv) { // Extract the script from the package. - char* package_data = argv[3]; + const char* package_filename = argv[3]; + MemMapping map; + if (sysMapFile(package_filename, &map) != 0) { + printf("failed to map package %s\n", argv[3]); + return 3; + } ZipArchive za; int err; - err = mzOpenZipArchive(package_data, &za); + err = mzOpenZipArchive(map.addr, map.length, &za); if (err != 0) { printf("failed to open package %s: %s\n", - package_data, strerror(err)); + argv[3], strerror(err)); return 3; } const ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME); if (script_entry == NULL) { - printf("failed to find %s in %s\n", SCRIPT_NAME, package_data); + printf("failed to find %s in %s\n", SCRIPT_NAME, package_filename); return 4; } @@ -152,6 +158,7 @@ int main(int argc, char** argv) { if (updater_info.package_zip) { mzCloseZipArchive(updater_info.package_zip); } + sysReleaseMap(&map); free(script); return 0; -- cgit v1.2.3 From 52b4036eb820042d0309b32b579c52b63ca58b4d Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 10 Feb 2014 15:30:30 -0800 Subject: add syspatch support to updater Add the syspatch() function, which can apply xdelta3+xz patches using the libsyspatch library. Change-Id: Idc1921e449020923bcaf425a1983bec0833e47ed --- updater/Android.mk | 2 + updater/install.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 115 insertions(+), 14 deletions(-) (limited to 'updater') diff --git a/updater/Android.mk b/updater/Android.mk index 67e98ecd4..2e92504cc 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -35,6 +35,8 @@ LOCAL_STATIC_LIBRARIES += libcutils liblog libstdc++ libc LOCAL_STATIC_LIBRARIES += libselinux LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. +LOCAL_STATIC_LIBRARIES += libsyspatch libxz libxdelta3 + # Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function # named "Register_()". Here we emit a little C function that # gets #included by updater.c. It calls all those registration diff --git a/updater/install.c b/updater/install.c index aebd4f34b..2cf00bf13 100644 --- a/updater/install.c +++ b/updater/install.c @@ -45,11 +45,25 @@ #include "mtdutils/mounts.h" #include "mtdutils/mtdutils.h" #include "updater.h" +#include "syspatch.h" #ifdef USE_EXT4 #include "make_ext4fs.h" #endif +// Take a sha-1 digest and return it as a newly-allocated hex string. +static char* PrintSha1(uint8_t* digest) { + char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1); + int i; + const char* alphabet = "0123456789abcdef"; + for (i = 0; i < SHA_DIGEST_SIZE; ++i) { + buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf]; + buffer[i*2+1] = alphabet[digest[i] & 0xf]; + } + buffer[i*2] = '\0'; + return buffer; +} + // mount(fs_type, partition_type, location, mount_point) // // fs_type="yaffs2" partition_type="MTD" location=partition @@ -1053,8 +1067,104 @@ Value* ApplyPatchSpaceFn(const char* name, State* state, return StringValue(strdup(CacheSizeCheck(bytes) ? "" : "t")); } +// syspatch(file, size, tgt_sha1, init_sha1, patch) + +Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 5) { + return ErrorAbort(state, "%s(): expected 5 args, got %d", name, argc); + } + + char* filename; + char* filename_size_str; + char* target_sha1; + char* init_sha1; + char* patch_filename; + uint8_t target_digest[SHA_DIGEST_SIZE]; + uint8_t init_digest[SHA_DIGEST_SIZE]; + + if (ReadArgs(state, argv, 5, &filename, &filename_size_str, + &target_sha1, &init_sha1, &patch_filename) < 0) { + return NULL; + } + + if (ParseSha1(target_sha1, target_digest) != 0) { + printf("%s(): failed to parse '%s' as target SHA-1", name, target_sha1); + memset(target_digest, 0, SHA_DIGEST_SIZE); + } + if (ParseSha1(init_sha1, init_digest) != 0) { + printf("%s(): failed to parse '%s' as init SHA-1", name, init_sha1); + memset(init_digest, 0, SHA_DIGEST_SIZE); + } + + size_t len = strtoull(filename_size_str, NULL, 0); + + SHA_CTX ctx; + SHA_init(&ctx); + FILE* src = fopen(filename, "r"); + size_t pos = 0; + unsigned char buffer[4096]; + while (pos < len) { + size_t to_read = len - pos; + if (to_read > sizeof(buffer)) to_read = sizeof(buffer); + size_t read = fread(buffer, 1, to_read, src); + if (read <= 0) { + printf("%s(): short read after %zu bytes\n", name, pos); + break; + } + SHA_update(&ctx, buffer, read); + pos += read; + } + rewind(src); + uint8_t* digest = SHA_final(&ctx); + + char* hexdigest = PrintSha1(digest); + printf(" system partition sha1 = %s\n", hexdigest); + + if (memcmp(digest, target_digest, SHA_DIGEST_SIZE) == 0) { + printf("%s(): %s is already target\n", name, filename); + fclose(src); + goto done; + } + + if (memcmp(digest, init_digest, SHA_DIGEST_SIZE) != 0) { + return ErrorAbort(state, "%s(): %s in unknown state\n", name, filename); + } + + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + + const ZipEntry* entry = mzFindZipEntry(za, patch_filename); + if (entry == NULL) { + return ErrorAbort(state, "%s(): no %s in package\n", name, patch_filename); + } + + unsigned char* patch_data; + size_t patch_len; + if (!mzGetStoredEntry(za, entry, &patch_data, &patch_len)) { + return ErrorAbort(state, "%s(): failed to get %s entry\n", name, patch_filename); + } + + FILE* tgt = fopen(filename, "r+"); + + int ret = syspatch(src, patch_data, patch_len, tgt); + + fclose(src); + fclose(tgt); + + if (ret != 0) { + return ErrorAbort(state, "%s(): patching failed\n", name); + } + + done: + free(filename_size_str); + free(target_sha1); + free(init_sha1); + free(patch_filename); + return StringValue(filename); + +} + +// apply_patch(file, size, init_sha1, tgt_sha1, patch) -// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1_1, patch_1, ...) Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc < 6 || (argc % 2) == 1) { return ErrorAbort(state, "%s(): expected at least 6 args and an " @@ -1239,19 +1349,6 @@ Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) { return StringValue(strdup(buffer)); } -// Take a sha-1 digest and return it as a newly-allocated hex string. -static char* PrintSha1(uint8_t* digest) { - char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1); - int i; - const char* alphabet = "0123456789abcdef"; - for (i = 0; i < SHA_DIGEST_SIZE; ++i) { - buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf]; - buffer[i*2+1] = alphabet[digest[i] & 0xf]; - } - buffer[i*2] = '\0'; - return buffer; -} - // sha1_check(data) // to return the sha1 of the data (given in the format returned by // read_file). @@ -1469,6 +1566,8 @@ void RegisterInstallFunctions() { RegisterFunction("apply_patch_check", ApplyPatchCheckFn); RegisterFunction("apply_patch_space", ApplyPatchSpaceFn); + RegisterFunction("syspatch", SysPatchFn); + RegisterFunction("read_file", ReadFileFn); RegisterFunction("sha1_check", Sha1CheckFn); RegisterFunction("rename", RenameFn); -- cgit v1.2.3 From a1bc148c7c81f886426c253f2ea7beb0f301f6b0 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 13 Feb 2014 15:18:19 -0800 Subject: remove 'retouch' ASLR support Older versions of android supported an ASLR system where binaries were randomly twiddled at OTA install time. Remove support for this; we now use the ASLR support in the linux kernel. Change-Id: I8348eb0d6424692668dc1a00e2416fbef6c158a2 --- updater/install.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index 2cf00bf13..e85ba50ae 100644 --- a/updater/install.c +++ b/updater/install.c @@ -1419,7 +1419,7 @@ Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) { v->type = VAL_BLOB; FileContents fc; - if (LoadFileContents(filename, &fc, RETOUCH_DONT_MASK) != 0) { + if (LoadFileContents(filename, &fc) != 0) { free(filename); v->size = -1; v->data = NULL; -- cgit v1.2.3 From 0d32f259cddeaf46917bdc4af3514114c206dd76 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 13 Feb 2014 15:07:56 -0800 Subject: clean up some warnings when building recovery Change-Id: I1541534ee6978ddf8d548433986679ce9507d508 --- updater/Android.mk | 1 + updater/install.c | 6 +++--- updater/updater.c | 3 +-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'updater') diff --git a/updater/Android.mk b/updater/Android.mk index 2e92504cc..c21ff8ecd 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -20,6 +20,7 @@ LOCAL_SRC_FILES := $(updater_src_files) ifeq ($(TARGET_USERIMAGES_USE_EXT4), true) LOCAL_CFLAGS += -DUSE_EXT4 +LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_C_INCLUDES += system/extras/ext4_utils LOCAL_STATIC_LIBRARIES += \ libext4_utils_static \ diff --git a/updater/install.c b/updater/install.c index e85ba50ae..ccafad9c2 100644 --- a/updater/install.c +++ b/updater/install.c @@ -52,7 +52,7 @@ #endif // Take a sha-1 digest and return it as a newly-allocated hex string. -static char* PrintSha1(uint8_t* digest) { +static char* PrintSha1(const uint8_t* digest) { char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1); int i; const char* alphabet = "0123456789abcdef"; @@ -1115,9 +1115,9 @@ Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) { pos += read; } rewind(src); - uint8_t* digest = SHA_final(&ctx); + const uint8_t* digest = SHA_final(&ctx); - char* hexdigest = PrintSha1(digest); + const char* hexdigest = PrintSha1(digest); printf(" system partition sha1 = %s\n", hexdigest); if (memcmp(digest, target_digest, SHA_DIGEST_SIZE) == 0) { diff --git a/updater/updater.c b/updater/updater.c index 4e1cc9c38..b7af3e500 100644 --- a/updater/updater.c +++ b/updater/updater.c @@ -105,8 +105,7 @@ int main(int argc, char** argv) { Expr* root; int error_count = 0; - yy_scan_string(script); - int error = yyparse(&root, &error_count); + int error = parse_string(script, &root, &error_count); if (error != 0 || error_count > 0) { printf("%d parse errors\n", error_count); return 6; -- cgit v1.2.3 From 3eb681d1de4eb0a4807e851c323568ed3f360381 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 13 Feb 2014 15:49:35 -0800 Subject: remove remaining libminelf references Change-Id: Id38b08607829bccc031693cc03e60e849903b6f8 --- updater/Android.mk | 1 - 1 file changed, 1 deletion(-) (limited to 'updater') diff --git a/updater/Android.mk b/updater/Android.mk index 2e92504cc..ebcfef15b 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -30,7 +30,6 @@ endif LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS) LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz LOCAL_STATIC_LIBRARIES += libmincrypt libbz -LOCAL_STATIC_LIBRARIES += libminelf LOCAL_STATIC_LIBRARIES += libcutils liblog libstdc++ libc LOCAL_STATIC_LIBRARIES += libselinux LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. -- cgit v1.2.3 From 0ac1cbaa76f1ab88ed575b197b06d042e015c10e Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 20 Feb 2014 09:46:19 -0800 Subject: add flag for GPL license updater now depends on the GPL'd libraries libsyspatch and libxdelta3, so be careful when taking code from this directory. Change-Id: Ib6f8c50ce7052912b9d81ff96d095f778bf9a3d0 --- updater/MODULE_LICENSE_GPL | 0 updater/NOTICE | 339 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 339 insertions(+) create mode 100644 updater/MODULE_LICENSE_GPL create mode 100644 updater/NOTICE (limited to 'updater') diff --git a/updater/MODULE_LICENSE_GPL b/updater/MODULE_LICENSE_GPL new file mode 100644 index 000000000..e69de29bb diff --git a/updater/NOTICE b/updater/NOTICE new file mode 100644 index 000000000..e77696ae8 --- /dev/null +++ b/updater/NOTICE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. -- cgit v1.2.3 From c9d6e4ff514b91b0ef08facb4dd480f166beae84 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 24 Feb 2014 16:02:50 -0800 Subject: support don't-care maps when writing the system image Make package_extract_file() take an optional third argument which is the pathname (in the package zip) of a map of don't-care regions to skip over when writing the file. Modify syspatch() to take source and target don't-care maps and use them when patching the system partition. Add the wipe_block_device() function to do a discard of all data on the partition. Change-Id: I8c856054edfb6aab2f3e5177f16d9d78add20be4 --- updater/install.c | 234 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 172 insertions(+), 62 deletions(-) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index ccafad9c2..faa1bb2f5 100644 --- a/updater/install.c +++ b/updater/install.c @@ -46,9 +46,11 @@ #include "mtdutils/mtdutils.h" #include "updater.h" #include "syspatch.h" +#include "install.h" #ifdef USE_EXT4 #include "make_ext4fs.h" +#include "wipe.h" #endif // Take a sha-1 digest and return it as a newly-allocated hex string. @@ -428,6 +430,54 @@ Value* PackageExtractDirFn(const char* name, State* state, } +DontCareMap* ReadDontCareMapFromZip(ZipArchive* za, const char* path) { + const char* name = "ReadDontCareMapFromZip"; + + const ZipEntry* entry = mzFindZipEntry(za, path); + if (entry == NULL) { + printf("%s: no %s in package\n", name, path); + return NULL; + } + + size_t map_size = mzGetZipEntryUncompLen(entry); + char* map_data = malloc(map_size); + if (map_data == NULL) { + printf("%s: failed to allocate %zu bytes for %s\n", + name, map_size, path); + return NULL; + } + + if (!mzExtractZipEntryToBuffer(za, entry, (unsigned char*) map_data)) { + printf("%s: failed to read %s\n", name, path); + return NULL; + } + + char* p = map_data; + DontCareMap* map = (DontCareMap*) malloc(sizeof(DontCareMap)); + + map->block_size = strtoul(p, &p, 0); + if (map->block_size != 4096) { + printf("%s: unexpected block size %zu\n", name, map->block_size); + return NULL; + } + + map->region_count = strtoul(p, &p, 0); + map->regions = (int*) malloc(map->region_count * sizeof(int)); + + int i; + for (i = 0; i < map->region_count; ++i) { + map->regions[i] = strtoul(p, &p, 0); + } + + return map; +} + +bool MapWriter(const unsigned char* data, int dataLen, void* cookie) { + return write_with_map(data, dataLen, (MapState*) cookie) == dataLen; +} + +// package_extract_file(package_path, destination_path, map_path) +// or // package_extract_file(package_path, destination_path) // or // package_extract_file(package_path) @@ -435,19 +485,30 @@ Value* PackageExtractDirFn(const char* name, State* state, // function (the char* returned is actually a FileContents*). Value* PackageExtractFileFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 1 && argc != 2) { - return ErrorAbort(state, "%s() expects 1 or 2 args, got %d", + if (argc < 1 || argc > 3) { + return ErrorAbort(state, "%s() expects 1 or 2 or 3 args, got %d", name, argc); } bool success = false; - if (argc == 2) { - // The two-argument version extracts to a file. + if (argc >= 2) { + // The two-argument version extracts to a file; the three-arg + // version extracts to a file, skipping over regions in a + // don't care map. + + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; char* zip_path; char* dest_path; - if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; + char* map_path = NULL; + DontCareMap* map = NULL; + if (argc == 2) { + if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; + } else { + if (ReadArgs(state, argv, 3, &zip_path, &dest_path, &map_path) < 0) return NULL; + map = ReadDontCareMapFromZip(za, map_path); + if (map == NULL) goto done2; + } - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; const ZipEntry* entry = mzFindZipEntry(za, zip_path); if (entry == NULL) { printf("%s: no %s in package\n", name, zip_path); @@ -460,12 +521,26 @@ Value* PackageExtractFileFn(const char* name, State* state, name, dest_path, strerror(errno)); goto done2; } - success = mzExtractZipEntryToFile(za, entry, fileno(f)); + if (map) { + MapState state; + state.map = map; + state.cr = 0; + state.so_far = 0; + state.f = f; + success = mzProcessZipEntryContents(za, entry, MapWriter, &state); + } else { + success = mzExtractZipEntryToFile(za, entry, fileno(f)); + } fclose(f); done2: free(zip_path); free(dest_path); + free(map_path); + if (map) { + free(map->regions); + free(map); + } return StringValue(strdup(success ? "t" : "")); } else { // The one-argument version returns the contents of the file @@ -1067,23 +1142,48 @@ Value* ApplyPatchSpaceFn(const char* name, State* state, return StringValue(strdup(CacheSizeCheck(bytes) ? "" : "t")); } -// syspatch(file, size, tgt_sha1, init_sha1, patch) +bool CheckMappedFileSha1(FILE* f, DontCareMap* map, uint8_t* intended_digest) { + MapState state; + + state.f = f; + state.so_far = 0; + state.cr = 0; + state.map = map; + + SHA_CTX ctx; + SHA_init(&ctx); + + unsigned char buffer[32173]; + size_t bytes_read; + + while ((bytes_read = read_with_map(buffer, sizeof(buffer), &state)) > 0) { + SHA_update(&ctx, buffer, bytes_read); + } + const uint8_t* digest = SHA_final(&ctx); + + return memcmp(digest, intended_digest, SHA_DIGEST_SIZE) == 0; +} + + +// syspatch(file, tgt_mapfile, tgt_sha1, init_mapfile, init_sha1, patch) Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 5) { - return ErrorAbort(state, "%s(): expected 5 args, got %d", name, argc); + if (argc != 6) { + return ErrorAbort(state, "%s(): expected 6 args, got %d", name, argc); } char* filename; - char* filename_size_str; + char* target_mapfilename; char* target_sha1; + char* init_mapfilename; char* init_sha1; char* patch_filename; uint8_t target_digest[SHA_DIGEST_SIZE]; uint8_t init_digest[SHA_DIGEST_SIZE]; - if (ReadArgs(state, argv, 5, &filename, &filename_size_str, - &target_sha1, &init_sha1, &patch_filename) < 0) { + if (ReadArgs(state, argv, 6, &filename, + &target_mapfilename, &target_sha1, + &init_mapfilename, &init_sha1, &patch_filename) < 0) { return NULL; } @@ -1096,68 +1196,56 @@ Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) { memset(init_digest, 0, SHA_DIGEST_SIZE); } - size_t len = strtoull(filename_size_str, NULL, 0); - - SHA_CTX ctx; - SHA_init(&ctx); + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; FILE* src = fopen(filename, "r"); - size_t pos = 0; - unsigned char buffer[4096]; - while (pos < len) { - size_t to_read = len - pos; - if (to_read > sizeof(buffer)) to_read = sizeof(buffer); - size_t read = fread(buffer, 1, to_read, src); - if (read <= 0) { - printf("%s(): short read after %zu bytes\n", name, pos); - break; - } - SHA_update(&ctx, buffer, read); - pos += read; - } - rewind(src); - const uint8_t* digest = SHA_final(&ctx); - - const char* hexdigest = PrintSha1(digest); - printf(" system partition sha1 = %s\n", hexdigest); - if (memcmp(digest, target_digest, SHA_DIGEST_SIZE) == 0) { - printf("%s(): %s is already target\n", name, filename); - fclose(src); - goto done; - } + DontCareMap* init_map = ReadDontCareMapFromZip(za, init_mapfilename); + if (init_map == NULL) return ErrorAbort(state, "%s(): failed to read init map\n", name); + DontCareMap* target_map = ReadDontCareMapFromZip(za, target_mapfilename); + if (target_map == NULL) return ErrorAbort(state, "%s(): failed to read target map\n", name); - if (memcmp(digest, init_digest, SHA_DIGEST_SIZE) != 0) { - return ErrorAbort(state, "%s(): %s in unknown state\n", name, filename); - } + if (CheckMappedFileSha1(src, init_map, init_digest)) { + // If the partition contents match the init_digest, then we need to apply the patch. - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + rewind(src); - const ZipEntry* entry = mzFindZipEntry(za, patch_filename); - if (entry == NULL) { - return ErrorAbort(state, "%s(): no %s in package\n", name, patch_filename); - } + const ZipEntry* entry = mzFindZipEntry(za, patch_filename); + if (entry == NULL) { + return ErrorAbort(state, "%s(): no %s in package\n", name, patch_filename); + } - unsigned char* patch_data; - size_t patch_len; - if (!mzGetStoredEntry(za, entry, &patch_data, &patch_len)) { - return ErrorAbort(state, "%s(): failed to get %s entry\n", name, patch_filename); - } + unsigned char* patch_data; + size_t patch_len; + if (!mzGetStoredEntry(za, entry, &patch_data, &patch_len)) { + return ErrorAbort(state, "%s(): failed to get %s entry\n", name, patch_filename); + } - FILE* tgt = fopen(filename, "r+"); + FILE* tgt = fopen(filename, "r+"); - int ret = syspatch(src, patch_data, patch_len, tgt); + int ret = syspatch(src, init_map, patch_data, patch_len, tgt, target_map); - fclose(src); - fclose(tgt); + fclose(src); + fclose(tgt); - if (ret != 0) { - return ErrorAbort(state, "%s(): patching failed\n", name); + if (ret != 0) { + return ErrorAbort(state, "%s(): patching failed\n", name); + } + } else { + rewind(src); + if (CheckMappedFileSha1(src, target_map, target_digest)) { + // If the partition contents match the target already, we + // don't need to do anything. + printf("%s: output is already target\n", name); + } else { + return ErrorAbort(state, "%s(): %s in unknown state\n", name, filename); + } } - done: - free(filename_size_str); + done: free(target_sha1); + free(target_mapfilename); free(init_sha1); + free(init_mapfilename); free(patch_filename); return StringValue(filename); @@ -1516,7 +1604,7 @@ Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) { // Return the value most recently saved with SetStageFn. The argument // is the block device for the misc partition. Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 2) { + if (argc != 1) { return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); } @@ -1533,6 +1621,27 @@ Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) { return StringValue(strdup(buffer)); } +Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + + char* filename; + char* len_str; + if (ReadArgs(state, argv, 2, &filename, &len_str) < 0) return NULL; + + size_t len = strtoull(len_str, NULL, 0); + int fd = open(filename, O_WRONLY, 0644); + int success = wipe_block_device(fd, len); + + free(filename); + free(len_str); + + close(fd); + + return StringValue(strdup(success ? "t" : "")); +} + void RegisterInstallFunctions() { RegisterFunction("mount", MountFn); RegisterFunction("is_mounted", IsMountedFn); @@ -1566,6 +1675,7 @@ void RegisterInstallFunctions() { RegisterFunction("apply_patch_check", ApplyPatchCheckFn); RegisterFunction("apply_patch_space", ApplyPatchSpaceFn); + RegisterFunction("wipe_block_device", WipeBlockDeviceFn); RegisterFunction("syspatch", SysPatchFn); RegisterFunction("read_file", ReadFileFn); -- cgit v1.2.3 From f3bb31c32fa879ccce358c15c93b7bd8582d1756 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Fri, 14 Mar 2014 09:39:48 -0700 Subject: Recovery 64-bit compile issues Change-Id: I92d5abd1a628feab3b0246924fab7f97ba3b9d34 --- updater/install.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index 6a8a6b3fc..1455557ca 100644 --- a/updater/install.c +++ b/updater/install.c @@ -864,7 +864,7 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { buffer = malloc(st.st_size+1); if (buffer == NULL) { - ErrorAbort(state, "%s: failed to alloc %lld bytes", name, st.st_size+1); + ErrorAbort(state, "%s: failed to alloc %lld bytes", name, (long long)st.st_size+1); goto done; } @@ -877,7 +877,7 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { if (fread(buffer, 1, st.st_size, f) != st.st_size) { ErrorAbort(state, "%s: failed to read %lld bytes from %s", - name, st.st_size+1, filename); + name, (long long)st.st_size+1, filename); fclose(f); goto done; } -- cgit v1.2.3 From aa1a31e83d25e6c3c3371063704eeb14558a595e Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Fri, 25 Apr 2014 18:47:18 -0700 Subject: Allow lines without = signs. The new build.prop for Sprout includes lines of the format: import xxx.prop These can be safely ignored when reading the property file. Change-Id: Ia84a138e71461ffe8e591e88143b9787873def29 --- updater/install.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index 53f5e48cb..8defc7720 100644 --- a/updater/install.c +++ b/updater/install.c @@ -927,8 +927,8 @@ Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) { // file_getprop(file, key) // // interprets 'file' as a getprop-style file (key=value pairs, one -// per line, # comment lines and blank lines okay), and returns the value -// for 'key' (or "" if it isn't defined). +// per line. # comment lines,blank lines, lines without '=' ignored), +// and returns the value for 'key' (or "" if it isn't defined). Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; char* buffer = NULL; @@ -986,9 +986,7 @@ Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { char* equal = strchr(line, '='); if (equal == NULL) { - ErrorAbort(state, "%s: malformed line \"%s\": %s not a prop file?", - name, line, filename); - goto done; + continue; } // trim whitespace between key and '=' -- cgit v1.2.3 From c704e06ce596bd0a6de66b10b108aee95535468a Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 23 May 2014 08:40:35 -0700 Subject: disable async reboot during package installation The default recovery UI will reboot the device when the power key is pressed 7 times in a row, regardless of what recovery is doing. Disable this feature during package installation, to minimize the chance of corrupting the device due to a mid-install reboot. (Debug packages can explicitly request that the feature be reenabled.) Change-Id: I20f3ec240ecd344615d452005ff26d8dd7775acf --- updater/install.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index 8defc7720..e1071c90c 100644 --- a/updater/install.c +++ b/updater/install.c @@ -1640,6 +1640,15 @@ Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) return StringValue(strdup(success ? "t" : "")); } +Value* EnableRebootFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 0) { + return ErrorAbort(state, "%s() expects no args, got %d", name, argc); + } + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); + fprintf(ui->cmd_pipe, "enable_reboot\n"); + return StringValue(strdup("t")); +} + void RegisterInstallFunctions() { RegisterFunction("mount", MountFn); RegisterFunction("is_mounted", IsMountedFn); @@ -1689,4 +1698,6 @@ void RegisterInstallFunctions() { RegisterFunction("reboot_now", RebootNowFn); RegisterFunction("get_stage", GetStageFn); RegisterFunction("set_stage", SetStageFn); + + RegisterFunction("enable_reboot", EnableRebootFn); } -- cgit v1.2.3 From 43772d26a5d8d31fd092a21edfca346f3b3901e7 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 9 Jun 2014 14:13:19 -0700 Subject: advance progress bar during block OTA installations While executing syspatch and package_extract_file() calls with don't care maps (both of which are used to rewrite the system image in incremental and full block OTAs, respectively), pass a progress callback in and use it to update the visible progress bar. Change-Id: I1d3742d167c1bb2130571eb5103b7795c65ff371 --- updater/install.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index e1071c90c..42f228972 100644 --- a/updater/install.c +++ b/updater/install.c @@ -463,17 +463,31 @@ DontCareMap* ReadDontCareMapFromZip(ZipArchive* za, const char* path) { map->region_count = strtoul(p, &p, 0); map->regions = (int*) malloc(map->region_count * sizeof(int)); + map->total_blocks = 0; int i; for (i = 0; i < map->region_count; ++i) { map->regions[i] = strtoul(p, &p, 0); + map->total_blocks += map->regions[i]; } return map; } +static FILE* mapwrite_cmd_pipe; + +static void progress_cb(long done, long total) { + if (total > 0) { + double frac = (double)done / total; + fprintf(mapwrite_cmd_pipe, "set_progress %f\n", frac); + fflush(mapwrite_cmd_pipe); + } +} + + + bool MapWriter(const unsigned char* data, int dataLen, void* cookie) { - return write_with_map(data, dataLen, (MapState*) cookie) == dataLen; + return write_with_map(data, dataLen, (MapState*) cookie, progress_cb) == dataLen; } // package_extract_file(package_path, destination_path, map_path) @@ -490,6 +504,10 @@ Value* PackageExtractFileFn(const char* name, State* state, name, argc); } bool success = false; + + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); + mapwrite_cmd_pipe = ui->cmd_pipe; + if (argc >= 2) { // The two-argument version extracts to a file; the three-arg // version extracts to a file, skipping over regions in a @@ -1185,6 +1203,9 @@ Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) { return NULL; } + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); + mapwrite_cmd_pipe = ui->cmd_pipe; + if (ParseSha1(target_sha1, target_digest) != 0) { printf("%s(): failed to parse '%s' as target SHA-1", name, target_sha1); memset(target_digest, 0, SHA_DIGEST_SIZE); @@ -1220,7 +1241,7 @@ Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) { FILE* tgt = fopen(filename, "r+"); - int ret = syspatch(src, init_map, patch_data, patch_len, tgt, target_map); + int ret = syspatch(src, init_map, patch_data, patch_len, tgt, target_map, progress_cb); fclose(src); fclose(tgt); -- cgit v1.2.3 From 37aedb3fafcccd0da5bd9089987f05895c27492d Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 16 Jun 2014 19:07:39 -0700 Subject: Support F2FS for the data partition This adds F2FS support - for wiping a device - for the install "format" command. Note: crypto data in "footer" with a default/negative length is not supported, unlike with "ext4". Change-Id: I8d141a0d4d14df9fe84d3b131484e9696fcd8870 Signed-off-by: JP Abgrall --- updater/install.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index 42f228972..edc386dc6 100644 --- a/updater/install.c +++ b/updater/install.c @@ -211,14 +211,29 @@ done: return StringValue(result); } +static int exec_cmd(const char* path, char* const argv[]) { + int status; + pid_t child; + if ((child = vfork()) == 0) { + execv(path, argv); + _exit(-1); + } + waitpid(child, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + printf("%s failed with status %d\n", path, WEXITSTATUS(status)); + } + return WEXITSTATUS(status); +} + // format(fs_type, partition_type, location, fs_size, mount_point) // // fs_type="yaffs2" partition_type="MTD" location=partition fs_size= mount_point= // fs_type="ext4" partition_type="EMMC" location=device fs_size= mount_point= -// if fs_size == 0, then make_ext4fs uses the entire partition. +// fs_type="f2fs" partition_type="EMMC" location=device fs_size= mount_point= +// if fs_size == 0, then make fs uses the entire partition. // if fs_size > 0, that is the size to use -// if fs_size < 0, then reserve that many bytes at the end of the partition +// if fs_size < 0, then reserve that many bytes at the end of the partition (not for "f2fs") Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 5) { @@ -290,6 +305,24 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { goto done; } result = location; + } else if (strcmp(fs_type, "f2fs") == 0) { + char *num_sectors; + if (asprintf(&num_sectors, "%lld", atoll(fs_size) / 512) <= 0) { + printf("format_volume: failed to create %s command for %s\n", fs_type, location); + result = strdup(""); + goto done; + } + const char *f2fs_path = "/sbin/mkfs.f2fs"; + const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", location, num_sectors, NULL}; + int status = exec_cmd(f2fs_path, (char* const*)f2fs_argv); + free(num_sectors); + if (status != 0) { + printf("%s: mkfs.f2fs failed (%d) on %s", + name, status, location); + result = strdup(""); + goto done; + } + result = location; #endif } else { printf("%s: unsupported fs_type \"%s\" partition_type \"%s\"", -- cgit v1.2.3 From a91ecc59b2753a3dcc93b2619559a980074e77bd Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Mon, 21 Jul 2014 17:40:02 -0700 Subject: Auto create parent directories for rename support Sometimes renames will move a file into a directory that does not yet exist. This will create the parent directories, using the same symlink logic, to ensure that there is a valid destination. Change-Id: Iaa005a12ce800c39f4db20f7c25a2a68cb40a52d --- updater/install.c | 8 +++++--- updater/install.h | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index edc386dc6..5025881d2 100644 --- a/updater/install.c +++ b/updater/install.c @@ -357,8 +357,10 @@ Value* RenameFn(const char* name, State* state, int argc, Expr* argv[]) { name); goto done; } - - if (rename(src_name, dst_name) != 0) { + if (make_parents(dst_name) != 0) { + ErrorAbort(state, "Creating parent of %s() failed, error %s()", + dst_name, strerror(errno)); + } else if (rename(src_name, dst_name) != 0) { ErrorAbort(state, "Rename of %s() to %s() failed, error %s()", src_name, dst_name, strerror(errno)); } else { @@ -642,7 +644,7 @@ static int make_parents(char* name) { *p = '\0'; if (make_parents(name) < 0) return -1; int result = mkdir(name, 0700); - if (result == 0) printf("symlink(): created [%s]\n", name); + if (result == 0) printf("created [%s]\n", name); *p = '/'; if (result == 0 || errno == EEXIST) { // successfully created or already existed; we're done diff --git a/updater/install.h b/updater/install.h index 94f344f8e..659c8b41c 100644 --- a/updater/install.h +++ b/updater/install.h @@ -19,4 +19,6 @@ void RegisterInstallFunctions(); +static int make_parents(char* name); + #endif -- cgit v1.2.3 From 2b5f0e0f767ce51d9605809052ad04fe83d1df83 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 6 Aug 2014 08:25:03 -0700 Subject: remove spurious parens from error message These error messages include empty parens after each string substition. Ill-advised cut and paste, probably. Bug: 16467401 Change-Id: Ib623172d6228354afdcc2e33442cc53a07f0ecbc --- updater/install.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index 5025881d2..198618001 100644 --- a/updater/install.c +++ b/updater/install.c @@ -353,15 +353,14 @@ Value* RenameFn(const char* name, State* state, int argc, Expr* argv[]) { goto done; } if (strlen(dst_name) == 0) { - ErrorAbort(state, "dst_name argument to %s() can't be empty", - name); + ErrorAbort(state, "dst_name argument to %s() can't be empty", name); goto done; } if (make_parents(dst_name) != 0) { - ErrorAbort(state, "Creating parent of %s() failed, error %s()", + ErrorAbort(state, "Creating parent of %s failed, error %s", dst_name, strerror(errno)); } else if (rename(src_name, dst_name) != 0) { - ErrorAbort(state, "Rename of %s() to %s() failed, error %s()", + ErrorAbort(state, "Rename of %s to %s failed, error %s", src_name, dst_name, strerror(errno)); } else { result = dst_name; -- cgit v1.2.3 From bc7ffeda98a861e346c30c771d3258030f7fcf21 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 15 Aug 2014 14:31:52 -0700 Subject: installer for new block OTA system (Cherry-pick back from master.) Bug: 16984795 Change-Id: Ifa3d8345c5e2a0be86fb28faa080ca82592a96b4 --- updater/Android.mk | 1 + updater/blockimg.c | 631 +++++++++++++++++++++++++++++++++++++++++++++++++++++ updater/blockimg.h | 22 ++ updater/install.c | 2 +- updater/updater.c | 4 + updater/updater.h | 3 + 6 files changed, 662 insertions(+), 1 deletion(-) create mode 100644 updater/blockimg.c create mode 100644 updater/blockimg.h (limited to 'updater') diff --git a/updater/Android.mk b/updater/Android.mk index 99b489029..b183c9221 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -4,6 +4,7 @@ LOCAL_PATH := $(call my-dir) updater_src_files := \ install.c \ + blockimg.c \ updater.c # diff --git a/updater/blockimg.c b/updater/blockimg.c new file mode 100644 index 000000000..c442ab22a --- /dev/null +++ b/updater/blockimg.c @@ -0,0 +1,631 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "applypatch/applypatch.h" +#include "edify/expr.h" +#include "mincrypt/sha.h" +#include "minzip/DirUtil.h" +#include "updater.h" + +#define BLOCKSIZE 4096 + +// Set this to 1 to interpret 'erase' transfers to mean do a +// BLKDISCARD ioctl. Set to 0 to interpret erase to mean fill the +// region with zeroes. +#define DEBUG_ERASE 0 + +#ifndef BLKDISCARD +#define BLKDISCARD _IO(0x12,119) +#endif + +char* PrintSha1(const uint8_t* digest); + +typedef struct { + int count; + int size; + int pos[0]; +} RangeSet; + +static RangeSet* parse_range(char* text) { + char* save; + int num; + num = strtol(strtok_r(text, ",", &save), NULL, 0); + + RangeSet* out = malloc(sizeof(RangeSet) + num * sizeof(int)); + if (out == NULL) { + fprintf(stderr, "failed to allocate range of %d bytes\n", + sizeof(RangeSet) + num * sizeof(int)); + exit(1); + } + out->count = num / 2; + out->size = 0; + int i; + for (i = 0; i < num; ++i) { + out->pos[i] = strtol(strtok_r(NULL, ",", &save), NULL, 0); + if (i%2) { + out->size += out->pos[i]; + } else { + out->size -= out->pos[i]; + } + } + + return out; +} + +static void readblock(int fd, uint8_t* data, size_t size) { + size_t so_far = 0; + while (so_far < size) { + ssize_t r = read(fd, data+so_far, size-so_far); + if (r < 0 && errno != EINTR) { + fprintf(stderr, "read failed: %s\n", strerror(errno)); + return; + } else { + so_far += r; + } + } +} + +static void writeblock(int fd, const uint8_t* data, size_t size) { + size_t written = 0; + while (written < size) { + ssize_t w = write(fd, data+written, size-written); + if (w < 0 && errno != EINTR) { + fprintf(stderr, "write failed: %s\n", strerror(errno)); + return; + } else { + written += w; + } + } +} + +static void check_lseek(int fd, off_t offset, int whence) { + while (true) { + int ret = lseek(fd, offset, whence); + if (ret < 0) { + if (errno != EINTR) { + fprintf(stderr, "lseek failed: %s\n", strerror(errno)); + exit(1); + } + } else { + break; + } + } +} + +static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) { + // if the buffer's big enough, reuse it. + if (size <= *buffer_alloc) return; + + free(*buffer); + + *buffer = (uint8_t*) malloc(size); + if (*buffer == NULL) { + fprintf(stderr, "failed to allocate %d bytes\n", size); + exit(1); + } + *buffer_alloc = size; +} + +typedef struct { + int fd; + RangeSet* tgt; + int p_block; + size_t p_remain; +} RangeSinkState; + +static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { + RangeSinkState* rss = (RangeSinkState*) token; + + if (rss->p_remain <= 0) { + fprintf(stderr, "range sink write overrun"); + exit(1); + } + + ssize_t written = 0; + while (size > 0) { + size_t write_now = size; + if (rss->p_remain < write_now) write_now = rss->p_remain; + writeblock(rss->fd, data, write_now); + data += write_now; + size -= write_now; + + rss->p_remain -= write_now; + written += write_now; + + if (rss->p_remain == 0) { + // move to the next block + ++rss->p_block; + if (rss->p_block < rss->tgt->count) { + rss->p_remain = (rss->tgt->pos[rss->p_block*2+1] - rss->tgt->pos[rss->p_block*2]) * BLOCKSIZE; + check_lseek(rss->fd, rss->tgt->pos[rss->p_block*2] * BLOCKSIZE, SEEK_SET); + } else { + // we can't write any more; return how many bytes have + // been written so far. + return written; + } + } + } + + return written; +} + +// All of the data for all the 'new' transfers is contained in one +// file in the update package, concatenated together in the order in +// which transfers.list will need it. We want to stream it out of the +// archive (it's compressed) without writing it to a temp file, but we +// can't write each section until it's that transfer's turn to go. +// +// To achieve this, we expand the new data from the archive in a +// background thread, and block that threads 'receive uncompressed +// data' function until the main thread has reached a point where we +// want some new data to be written. We signal the background thread +// with the destination for the data and block the main thread, +// waiting for the background thread to complete writing that section. +// Then it signals the main thread to wake up and goes back to +// blocking waiting for a transfer. +// +// NewThreadInfo is the struct used to pass information back and forth +// between the two threads. When the main thread wants some data +// written, it sets rss to the destination location and signals the +// condition. When the background thread is done writing, it clears +// rss and signals the condition again. + +typedef struct { + ZipArchive* za; + const ZipEntry* entry; + + RangeSinkState* rss; + + pthread_mutex_t mu; + pthread_cond_t cv; +} NewThreadInfo; + +static bool receive_new_data(const unsigned char* data, int size, void* cookie) { + NewThreadInfo* nti = (NewThreadInfo*) cookie; + + while (size > 0) { + // Wait for nti->rss to be non-NULL, indicating some of this + // data is wanted. + pthread_mutex_lock(&nti->mu); + while (nti->rss == NULL) { + pthread_cond_wait(&nti->cv, &nti->mu); + } + pthread_mutex_unlock(&nti->mu); + + // At this point nti->rss is set, and we own it. The main + // thread is waiting for it to disappear from nti. + ssize_t written = RangeSinkWrite(data, size, nti->rss); + data += written; + size -= written; + + if (nti->rss->p_block == nti->rss->tgt->count) { + // we have written all the bytes desired by this rss. + + pthread_mutex_lock(&nti->mu); + nti->rss = NULL; + pthread_cond_broadcast(&nti->cv); + pthread_mutex_unlock(&nti->mu); + } + } + + return true; +} + +static void* unzip_new_data(void* cookie) { + NewThreadInfo* nti = (NewThreadInfo*) cookie; + mzProcessZipEntryContents(nti->za, nti->entry, receive_new_data, nti); + return NULL; +} + +// args: +// - block device (or file) to modify in-place +// - transfer list (blob) +// - new data stream (filename within package.zip) +// - patch stream (filename within package.zip, must be uncompressed) + +Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) { + Value* blockdev_filename; + Value* transfer_list; + Value* new_data_fn; + Value* patch_data_fn; + bool success = false; + + if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list, + &new_data_fn, &patch_data_fn) < 0) { + return NULL; + } + + if (blockdev_filename->type != VAL_STRING) { + ErrorAbort(state, "blockdev_filename argument to %s must be string", name); + goto done; + } + if (transfer_list->type != VAL_BLOB) { + ErrorAbort(state, "transfer_list argument to %s must be blob", name); + goto done; + } + if (new_data_fn->type != VAL_STRING) { + ErrorAbort(state, "new_data_fn argument to %s must be string", name); + goto done; + } + if (patch_data_fn->type != VAL_STRING) { + ErrorAbort(state, "patch_data_fn argument to %s must be string", name); + goto done; + } + + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); + FILE* cmd_pipe = ui->cmd_pipe; + + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + + const ZipEntry* patch_entry = mzFindZipEntry(za, patch_data_fn->data); + if (patch_entry == NULL) { + ErrorAbort(state, "%s(): no file \"%s\" in package", name, patch_data_fn->data); + goto done; + } + + uint8_t* patch_start = ((UpdaterInfo*)(state->cookie))->package_zip_addr + + mzGetZipEntryOffset(patch_entry); + + const ZipEntry* new_entry = mzFindZipEntry(za, new_data_fn->data); + if (new_entry == NULL) { + ErrorAbort(state, "%s(): no file \"%s\" in package", name, new_data_fn->data); + goto done; + } + + // The transfer list is a text file containing commands to + // transfer data from one place to another on the target + // partition. We parse it and execute the commands in order: + // + // zero [rangeset] + // - fill the indicated blocks with zeros + // + // new [rangeset] + // - fill the blocks with data read from the new_data file + // + // bsdiff patchstart patchlen [src rangeset] [tgt rangeset] + // imgdiff patchstart patchlen [src rangeset] [tgt rangeset] + // - read the source blocks, apply a patch, write result to + // target blocks. bsdiff or imgdiff specifies the type of + // patch. + // + // move [src rangeset] [tgt rangeset] + // - copy data from source blocks to target blocks (no patch + // needed; rangesets are the same size) + // + // erase [rangeset] + // - mark the given blocks as empty + // + // The creator of the transfer list will guarantee that no block + // is read (ie, used as the source for a patch or move) after it + // has been written. + // + // Within one command the source and target ranges may overlap so + // in general we need to read the entire source into memory before + // writing anything to the target blocks. + // + // All the patch data is concatenated into one patch_data file in + // the update package. It must be stored uncompressed because we + // memory-map it in directly from the archive. (Since patches are + // already compressed, we lose very little by not compressing + // their concatenation.) + + pthread_t new_data_thread; + NewThreadInfo nti; + nti.za = za; + nti.entry = new_entry; + nti.rss = NULL; + pthread_mutex_init(&nti.mu, NULL); + pthread_cond_init(&nti.cv, NULL); + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_create(&new_data_thread, &attr, unzip_new_data, &nti); + + int i, j; + + char* linesave; + char* wordsave; + + int fd = open(blockdev_filename->data, O_RDWR); + if (fd < 0) { + ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno)); + goto done; + } + + char* line; + char* word; + + line = strtok_r(transfer_list->data, "\n", &linesave); + + // first line in transfer list is the version number; currently + // there's only version 1. + if (strcmp(line, "1") != 0) { + ErrorAbort(state, "unexpected transfer list version [%s]\n", line); + goto done; + } + + // second line in transfer list is the total number of blocks we + // expect to write. + line = strtok_r(NULL, "\n", &linesave); + int total_blocks = strtol(line, NULL, 0); + // shouldn't happen, but avoid divide by zero. + if (total_blocks == 0) ++total_blocks; + int blocks_so_far = 0; + + uint8_t* buffer = NULL; + size_t buffer_alloc = 0; + + // third and subsequent lines are all individual transfer commands. + for (line = strtok_r(NULL, "\n", &linesave); line; + line = strtok_r(NULL, "\n", &linesave)) { + char* style; + style = strtok_r(line, " ", &wordsave); + + if (strcmp("move", style) == 0) { + word = strtok_r(NULL, " ", &wordsave); + RangeSet* src = parse_range(word); + word = strtok_r(NULL, " ", &wordsave); + RangeSet* tgt = parse_range(word); + + printf(" moving %d blocks\n", src->size); + + allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); + size_t p = 0; + for (i = 0; i < src->count; ++i) { + check_lseek(fd, src->pos[i*2] * BLOCKSIZE, SEEK_SET); + size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; + readblock(fd, buffer+p, sz); + p += sz; + } + + p = 0; + for (i = 0; i < tgt->count; ++i) { + check_lseek(fd, tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); + size_t sz = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE; + writeblock(fd, buffer+p, sz); + p += sz; + } + + blocks_so_far += tgt->size; + fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); + fflush(cmd_pipe); + + free(src); + free(tgt); + + } else if (strcmp("zero", style) == 0 || + (DEBUG_ERASE && strcmp("erase", style) == 0)) { + word = strtok_r(NULL, " ", &wordsave); + RangeSet* tgt = parse_range(word); + + printf(" zeroing %d blocks\n", tgt->size); + + allocate(BLOCKSIZE, &buffer, &buffer_alloc); + memset(buffer, 0, BLOCKSIZE); + for (i = 0; i < tgt->count; ++i) { + check_lseek(fd, tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); + for (j = tgt->pos[i*2]; j < tgt->pos[i*2+1]; ++j) { + writeblock(fd, buffer, BLOCKSIZE); + } + } + + if (style[0] == 'z') { // "zero" but not "erase" + blocks_so_far += tgt->size; + fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); + fflush(cmd_pipe); + } + + free(tgt); + } else if (strcmp("new", style) == 0) { + + word = strtok_r(NULL, " ", &wordsave); + RangeSet* tgt = parse_range(word); + + printf(" writing %d blocks of new data\n", tgt->size); + + RangeSinkState rss; + rss.fd = fd; + rss.tgt = tgt; + rss.p_block = 0; + rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; + check_lseek(fd, tgt->pos[0] * BLOCKSIZE, SEEK_SET); + + pthread_mutex_lock(&nti.mu); + nti.rss = &rss; + pthread_cond_broadcast(&nti.cv); + while (nti.rss) { + pthread_cond_wait(&nti.cv, &nti.mu); + } + pthread_mutex_unlock(&nti.mu); + + blocks_so_far += tgt->size; + fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); + fflush(cmd_pipe); + + free(tgt); + + } else if (strcmp("bsdiff", style) == 0 || + strcmp("imgdiff", style) == 0) { + word = strtok_r(NULL, " ", &wordsave); + size_t patch_offset = strtoul(word, NULL, 0); + word = strtok_r(NULL, " ", &wordsave); + size_t patch_len = strtoul(word, NULL, 0); + + word = strtok_r(NULL, " ", &wordsave); + RangeSet* src = parse_range(word); + word = strtok_r(NULL, " ", &wordsave); + RangeSet* tgt = parse_range(word); + + printf(" patching %d blocks to %d\n", src->size, tgt->size); + + // Read the source into memory. + allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); + size_t p = 0; + for (i = 0; i < src->count; ++i) { + check_lseek(fd, src->pos[i*2] * BLOCKSIZE, SEEK_SET); + size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; + readblock(fd, buffer+p, sz); + p += sz; + } + + Value patch_value; + patch_value.type = VAL_BLOB; + patch_value.size = patch_len; + patch_value.data = (char*)(patch_start + patch_offset); + + RangeSinkState rss; + rss.fd = fd; + rss.tgt = tgt; + rss.p_block = 0; + rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; + check_lseek(fd, tgt->pos[0] * BLOCKSIZE, SEEK_SET); + + if (style[0] == 'i') { // imgdiff + ApplyImagePatch(buffer, src->size * BLOCKSIZE, + &patch_value, + &RangeSinkWrite, &rss, NULL, NULL); + } else { + ApplyBSDiffPatch(buffer, src->size * BLOCKSIZE, + &patch_value, 0, + &RangeSinkWrite, &rss, NULL); + } + + // We expect the output of the patcher to fill the tgt ranges exactly. + if (rss.p_block != tgt->count || rss.p_remain != 0) { + fprintf(stderr, "range sink underrun?\n"); + } + + blocks_so_far += tgt->size; + fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); + fflush(cmd_pipe); + + free(src); + free(tgt); + } else if (!DEBUG_ERASE && strcmp("erase", style) == 0) { + struct stat st; + if (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)) { + word = strtok_r(NULL, " ", &wordsave); + RangeSet* tgt = parse_range(word); + + printf(" erasing %d blocks\n", tgt->size); + + for (i = 0; i < tgt->count; ++i) { + uint64_t range[2]; + // offset in bytes + range[0] = tgt->pos[i*2] * BLOCKSIZE; + // len in bytes + range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE; + + if (ioctl(fd, BLKDISCARD, &range) < 0) { + printf(" blkdiscard failed: %s\n", strerror(errno)); + } + } + + free(tgt); + } else { + printf(" ignoring erase (not block device)\n"); + } + } else { + fprintf(stderr, "unknown transfer style \"%s\"\n", style); + exit(1); + } + } + + pthread_join(new_data_thread, NULL); + success = true; + + free(buffer); + printf("wrote %d blocks; expected %d\n", blocks_so_far, total_blocks); + printf("max alloc needed was %zu\n", buffer_alloc); + + done: + FreeValue(blockdev_filename); + FreeValue(transfer_list); + FreeValue(new_data_fn); + FreeValue(patch_data_fn); + return StringValue(success ? strdup("t") : strdup("")); +} + +Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) { + Value* blockdev_filename; + Value* ranges; + const uint8_t* digest = NULL; + if (ReadValueArgs(state, argv, 2, &blockdev_filename, &ranges) < 0) { + return NULL; + } + + if (blockdev_filename->type != VAL_STRING) { + ErrorAbort(state, "blockdev_filename argument to %s must be string", name); + goto done; + } + if (ranges->type != VAL_STRING) { + ErrorAbort(state, "ranges argument to %s must be string", name); + goto done; + } + + int fd = open(blockdev_filename->data, O_RDWR); + if (fd < 0) { + ErrorAbort(state, "failed to open %s: %s", blockdev_filename->data, strerror(errno)); + goto done; + } + + RangeSet* rs = parse_range(ranges->data); + uint8_t buffer[BLOCKSIZE]; + + SHA_CTX ctx; + SHA_init(&ctx); + + int i, j; + for (i = 0; i < rs->count; ++i) { + check_lseek(fd, rs->pos[i*2] * BLOCKSIZE, SEEK_SET); + for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) { + readblock(fd, buffer, BLOCKSIZE); + SHA_update(&ctx, buffer, BLOCKSIZE); + } + } + digest = SHA_final(&ctx); + close(fd); + + done: + FreeValue(blockdev_filename); + FreeValue(ranges); + if (digest == NULL) { + return StringValue(strdup("")); + } else { + return StringValue(PrintSha1(digest)); + } +} + +void RegisterBlockImageFunctions() { + RegisterFunction("block_image_update", BlockImageUpdateFn); + RegisterFunction("range_sha1", RangeSha1Fn); +} diff --git a/updater/blockimg.h b/updater/blockimg.h new file mode 100644 index 000000000..2f4ad3c04 --- /dev/null +++ b/updater/blockimg.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UPDATER_BLOCKIMG_H_ +#define _UPDATER_BLOCKIMG_H_ + +void RegisterBlockImageFunctions(); + +#endif diff --git a/updater/install.c b/updater/install.c index 198618001..cdcdb8fdb 100644 --- a/updater/install.c +++ b/updater/install.c @@ -54,7 +54,7 @@ #endif // Take a sha-1 digest and return it as a newly-allocated hex string. -static char* PrintSha1(const uint8_t* digest) { +char* PrintSha1(const uint8_t* digest) { char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1); int i; const char* alphabet = "0123456789abcdef"; diff --git a/updater/updater.c b/updater/updater.c index b7af3e500..465e1238e 100644 --- a/updater/updater.c +++ b/updater/updater.c @@ -21,6 +21,7 @@ #include "edify/expr.h" #include "updater.h" #include "install.h" +#include "blockimg.h" #include "minzip/Zip.h" #include "minzip/SysUtil.h" @@ -98,6 +99,7 @@ int main(int argc, char** argv) { RegisterBuiltins(); RegisterInstallFunctions(); + RegisterBlockImageFunctions(); RegisterDeviceExtensions(); FinishRegistration(); @@ -127,6 +129,8 @@ int main(int argc, char** argv) { updater_info.cmd_pipe = cmd_pipe; updater_info.package_zip = &za; updater_info.version = atoi(version); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; State state; state.cookie = &updater_info; diff --git a/updater/updater.h b/updater/updater.h index d2e901141..d1dfdd05e 100644 --- a/updater/updater.h +++ b/updater/updater.h @@ -27,6 +27,9 @@ typedef struct { FILE* cmd_pipe; ZipArchive* package_zip; int version; + + uint8_t* package_zip_addr; + size_t package_zip_len; } UpdaterInfo; extern struct selabel_handle *sehandle; -- cgit v1.2.3 From 1d5d6098f4a470bc8e56ae8914180041815e6e22 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 21 Aug 2014 10:47:24 -0700 Subject: fix two bugs in block image updater The computation of file offsets was overflowing for partitions larger than 2 GB. The parsing of the transfer file could fail at the end if the data happened to not be properly null-terminated. Bug: 16984795 Change-Id: I3ce6eb3e54ab7b55aa9bbed252da5a7eacd3317a --- updater/blockimg.c | 50 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 18 deletions(-) (limited to 'updater') diff --git a/updater/blockimg.c b/updater/blockimg.c index c442ab22a..6d412d5d5 100644 --- a/updater/blockimg.c +++ b/updater/blockimg.c @@ -61,7 +61,7 @@ static RangeSet* parse_range(char* text) { RangeSet* out = malloc(sizeof(RangeSet) + num * sizeof(int)); if (out == NULL) { - fprintf(stderr, "failed to allocate range of %d bytes\n", + fprintf(stderr, "failed to allocate range of %lu bytes\n", sizeof(RangeSet) + num * sizeof(int)); exit(1); } @@ -108,7 +108,7 @@ static void writeblock(int fd, const uint8_t* data, size_t size) { static void check_lseek(int fd, off_t offset, int whence) { while (true) { - int ret = lseek(fd, offset, whence); + off_t ret = lseek(fd, offset, whence); if (ret < 0) { if (errno != EINTR) { fprintf(stderr, "lseek failed: %s\n", strerror(errno)); @@ -128,7 +128,7 @@ static void allocate(size_t size, uint8_t** buffer, size_t* buffer_alloc) { *buffer = (uint8_t*) malloc(size); if (*buffer == NULL) { - fprintf(stderr, "failed to allocate %d bytes\n", size); + fprintf(stderr, "failed to allocate %zu bytes\n", size); exit(1); } *buffer_alloc = size; @@ -165,7 +165,7 @@ static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { ++rss->p_block; if (rss->p_block < rss->tgt->count) { rss->p_remain = (rss->tgt->pos[rss->p_block*2+1] - rss->tgt->pos[rss->p_block*2]) * BLOCKSIZE; - check_lseek(rss->fd, rss->tgt->pos[rss->p_block*2] * BLOCKSIZE, SEEK_SET); + check_lseek(rss->fd, rss->tgt->pos[rss->p_block*2] * (off_t)BLOCKSIZE, SEEK_SET); } else { // we can't write any more; return how many bytes have // been written so far. @@ -253,12 +253,13 @@ static void* unzip_new_data(void* cookie) { Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) { Value* blockdev_filename; - Value* transfer_list; + Value* transfer_list_value; + char* transfer_list = NULL; Value* new_data_fn; Value* patch_data_fn; bool success = false; - if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list, + if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value, &new_data_fn, &patch_data_fn) < 0) { return NULL; } @@ -267,7 +268,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] ErrorAbort(state, "blockdev_filename argument to %s must be string", name); goto done; } - if (transfer_list->type != VAL_BLOB) { + if (transfer_list_value->type != VAL_BLOB) { ErrorAbort(state, "transfer_list argument to %s must be blob", name); goto done; } @@ -364,7 +365,19 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] char* line; char* word; - line = strtok_r(transfer_list->data, "\n", &linesave); + // The data in transfer_list_value is not necessarily + // null-terminated, so we need to copy it to a new buffer and add + // the null that strtok_r will need. + transfer_list = malloc(transfer_list_value->size+1); + if (transfer_list == NULL) { + fprintf(stderr, "failed to allocate %zd bytes for transfer list\n", + transfer_list_value->size+1); + exit(1); + } + memcpy(transfer_list, transfer_list_value->data, transfer_list_value->size); + transfer_list[transfer_list_value->size] = '\0'; + + line = strtok_r(transfer_list, "\n", &linesave); // first line in transfer list is the version number; currently // there's only version 1. @@ -401,7 +414,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); size_t p = 0; for (i = 0; i < src->count; ++i) { - check_lseek(fd, src->pos[i*2] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, src->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; readblock(fd, buffer+p, sz); p += sz; @@ -409,7 +422,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] p = 0; for (i = 0; i < tgt->count; ++i) { - check_lseek(fd, tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, tgt->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); size_t sz = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE; writeblock(fd, buffer+p, sz); p += sz; @@ -432,7 +445,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] allocate(BLOCKSIZE, &buffer, &buffer_alloc); memset(buffer, 0, BLOCKSIZE); for (i = 0; i < tgt->count; ++i) { - check_lseek(fd, tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, tgt->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); for (j = tgt->pos[i*2]; j < tgt->pos[i*2+1]; ++j) { writeblock(fd, buffer, BLOCKSIZE); } @@ -457,7 +470,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] rss.tgt = tgt; rss.p_block = 0; rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; - check_lseek(fd, tgt->pos[0] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, tgt->pos[0] * (off_t)BLOCKSIZE, SEEK_SET); pthread_mutex_lock(&nti.mu); nti.rss = &rss; @@ -491,7 +504,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); size_t p = 0; for (i = 0; i < src->count; ++i) { - check_lseek(fd, src->pos[i*2] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, src->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; readblock(fd, buffer+p, sz); p += sz; @@ -507,7 +520,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] rss.tgt = tgt; rss.p_block = 0; rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; - check_lseek(fd, tgt->pos[0] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, tgt->pos[0] * (off_t)BLOCKSIZE, SEEK_SET); if (style[0] == 'i') { // imgdiff ApplyImagePatch(buffer, src->size * BLOCKSIZE, @@ -541,9 +554,9 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] for (i = 0; i < tgt->count; ++i) { uint64_t range[2]; // offset in bytes - range[0] = tgt->pos[i*2] * BLOCKSIZE; + range[0] = tgt->pos[i*2] * (uint64_t)BLOCKSIZE; // len in bytes - range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE; + range[1] = (tgt->pos[i*2+1] - tgt->pos[i*2]) * (uint64_t)BLOCKSIZE; if (ioctl(fd, BLKDISCARD, &range) < 0) { printf(" blkdiscard failed: %s\n", strerror(errno)); @@ -568,8 +581,9 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] printf("max alloc needed was %zu\n", buffer_alloc); done: + free(transfer_list); FreeValue(blockdev_filename); - FreeValue(transfer_list); + FreeValue(transfer_list_value); FreeValue(new_data_fn); FreeValue(patch_data_fn); return StringValue(success ? strdup("t") : strdup("")); @@ -606,7 +620,7 @@ Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) { int i, j; for (i = 0; i < rs->count; ++i) { - check_lseek(fd, rs->pos[i*2] * BLOCKSIZE, SEEK_SET); + check_lseek(fd, rs->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) { readblock(fd, buffer, BLOCKSIZE); SHA_update(&ctx, buffer, BLOCKSIZE); -- cgit v1.2.3 From 5f875bf57739bf0b5c2d182173513233bdb71ab8 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 22 Aug 2014 14:53:43 -0700 Subject: remove code for original block OTA mechanism Superseded by newer code. Bug: 16984795 Change-Id: I842299f6a02af7ccf51ef2ca174d813ca53deef1 --- updater/Android.mk | 2 - updater/install.c | 213 ++--------------------------------------------------- 2 files changed, 6 insertions(+), 209 deletions(-) (limited to 'updater') diff --git a/updater/Android.mk b/updater/Android.mk index b183c9221..a3a900a80 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -36,8 +36,6 @@ LOCAL_STATIC_LIBRARIES += libcutils liblog libstdc++ libc LOCAL_STATIC_LIBRARIES += libselinux LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. -LOCAL_STATIC_LIBRARIES += libsyspatch libxz libxdelta3 - # Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function # named "Register_()". Here we emit a little C function that # gets #included by updater.c. It calls all those registration diff --git a/updater/install.c b/updater/install.c index cdcdb8fdb..dad0d08c9 100644 --- a/updater/install.c +++ b/updater/install.c @@ -45,7 +45,6 @@ #include "mtdutils/mounts.h" #include "mtdutils/mtdutils.h" #include "updater.h" -#include "syspatch.h" #include "install.h" #ifdef USE_EXT4 @@ -464,68 +463,6 @@ Value* PackageExtractDirFn(const char* name, State* state, } -DontCareMap* ReadDontCareMapFromZip(ZipArchive* za, const char* path) { - const char* name = "ReadDontCareMapFromZip"; - - const ZipEntry* entry = mzFindZipEntry(za, path); - if (entry == NULL) { - printf("%s: no %s in package\n", name, path); - return NULL; - } - - size_t map_size = mzGetZipEntryUncompLen(entry); - char* map_data = malloc(map_size); - if (map_data == NULL) { - printf("%s: failed to allocate %zu bytes for %s\n", - name, map_size, path); - return NULL; - } - - if (!mzExtractZipEntryToBuffer(za, entry, (unsigned char*) map_data)) { - printf("%s: failed to read %s\n", name, path); - return NULL; - } - - char* p = map_data; - DontCareMap* map = (DontCareMap*) malloc(sizeof(DontCareMap)); - - map->block_size = strtoul(p, &p, 0); - if (map->block_size != 4096) { - printf("%s: unexpected block size %zu\n", name, map->block_size); - return NULL; - } - - map->region_count = strtoul(p, &p, 0); - map->regions = (int*) malloc(map->region_count * sizeof(int)); - map->total_blocks = 0; - - int i; - for (i = 0; i < map->region_count; ++i) { - map->regions[i] = strtoul(p, &p, 0); - map->total_blocks += map->regions[i]; - } - - return map; -} - -static FILE* mapwrite_cmd_pipe; - -static void progress_cb(long done, long total) { - if (total > 0) { - double frac = (double)done / total; - fprintf(mapwrite_cmd_pipe, "set_progress %f\n", frac); - fflush(mapwrite_cmd_pipe); - } -} - - - -bool MapWriter(const unsigned char* data, int dataLen, void* cookie) { - return write_with_map(data, dataLen, (MapState*) cookie, progress_cb) == dataLen; -} - -// package_extract_file(package_path, destination_path, map_path) -// or // package_extract_file(package_path, destination_path) // or // package_extract_file(package_path) @@ -533,33 +470,22 @@ bool MapWriter(const unsigned char* data, int dataLen, void* cookie) { // function (the char* returned is actually a FileContents*). Value* PackageExtractFileFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc < 1 || argc > 3) { - return ErrorAbort(state, "%s() expects 1 or 2 or 3 args, got %d", + if (argc < 1 || argc > 2) { + return ErrorAbort(state, "%s() expects 1 or 2 args, got %d", name, argc); } bool success = false; UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); - mapwrite_cmd_pipe = ui->cmd_pipe; - if (argc >= 2) { - // The two-argument version extracts to a file; the three-arg - // version extracts to a file, skipping over regions in a - // don't care map. + if (argc == 2) { + // The two-argument version extracts to a file. ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; char* zip_path; char* dest_path; - char* map_path = NULL; - DontCareMap* map = NULL; - if (argc == 2) { - if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; - } else { - if (ReadArgs(state, argv, 3, &zip_path, &dest_path, &map_path) < 0) return NULL; - map = ReadDontCareMapFromZip(za, map_path); - if (map == NULL) goto done2; - } + if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; const ZipEntry* entry = mzFindZipEntry(za, zip_path); if (entry == NULL) { @@ -573,26 +499,12 @@ Value* PackageExtractFileFn(const char* name, State* state, name, dest_path, strerror(errno)); goto done2; } - if (map) { - MapState state; - state.map = map; - state.cr = 0; - state.so_far = 0; - state.f = f; - success = mzProcessZipEntryContents(za, entry, MapWriter, &state); - } else { - success = mzExtractZipEntryToFile(za, entry, fileno(f)); - } + success = mzExtractZipEntryToFile(za, entry, fileno(f)); fclose(f); done2: free(zip_path); free(dest_path); - free(map_path); - if (map) { - free(map->regions); - free(map); - } return StringValue(strdup(success ? "t" : "")); } else { // The one-argument version returns the contents of the file @@ -1192,118 +1104,6 @@ Value* ApplyPatchSpaceFn(const char* name, State* state, return StringValue(strdup(CacheSizeCheck(bytes) ? "" : "t")); } -bool CheckMappedFileSha1(FILE* f, DontCareMap* map, uint8_t* intended_digest) { - MapState state; - - state.f = f; - state.so_far = 0; - state.cr = 0; - state.map = map; - - SHA_CTX ctx; - SHA_init(&ctx); - - unsigned char buffer[32173]; - size_t bytes_read; - - while ((bytes_read = read_with_map(buffer, sizeof(buffer), &state)) > 0) { - SHA_update(&ctx, buffer, bytes_read); - } - const uint8_t* digest = SHA_final(&ctx); - - return memcmp(digest, intended_digest, SHA_DIGEST_SIZE) == 0; -} - - -// syspatch(file, tgt_mapfile, tgt_sha1, init_mapfile, init_sha1, patch) - -Value* SysPatchFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 6) { - return ErrorAbort(state, "%s(): expected 6 args, got %d", name, argc); - } - - char* filename; - char* target_mapfilename; - char* target_sha1; - char* init_mapfilename; - char* init_sha1; - char* patch_filename; - uint8_t target_digest[SHA_DIGEST_SIZE]; - uint8_t init_digest[SHA_DIGEST_SIZE]; - - if (ReadArgs(state, argv, 6, &filename, - &target_mapfilename, &target_sha1, - &init_mapfilename, &init_sha1, &patch_filename) < 0) { - return NULL; - } - - UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); - mapwrite_cmd_pipe = ui->cmd_pipe; - - if (ParseSha1(target_sha1, target_digest) != 0) { - printf("%s(): failed to parse '%s' as target SHA-1", name, target_sha1); - memset(target_digest, 0, SHA_DIGEST_SIZE); - } - if (ParseSha1(init_sha1, init_digest) != 0) { - printf("%s(): failed to parse '%s' as init SHA-1", name, init_sha1); - memset(init_digest, 0, SHA_DIGEST_SIZE); - } - - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; - FILE* src = fopen(filename, "r"); - - DontCareMap* init_map = ReadDontCareMapFromZip(za, init_mapfilename); - if (init_map == NULL) return ErrorAbort(state, "%s(): failed to read init map\n", name); - DontCareMap* target_map = ReadDontCareMapFromZip(za, target_mapfilename); - if (target_map == NULL) return ErrorAbort(state, "%s(): failed to read target map\n", name); - - if (CheckMappedFileSha1(src, init_map, init_digest)) { - // If the partition contents match the init_digest, then we need to apply the patch. - - rewind(src); - - const ZipEntry* entry = mzFindZipEntry(za, patch_filename); - if (entry == NULL) { - return ErrorAbort(state, "%s(): no %s in package\n", name, patch_filename); - } - - unsigned char* patch_data; - size_t patch_len; - if (!mzGetStoredEntry(za, entry, &patch_data, &patch_len)) { - return ErrorAbort(state, "%s(): failed to get %s entry\n", name, patch_filename); - } - - FILE* tgt = fopen(filename, "r+"); - - int ret = syspatch(src, init_map, patch_data, patch_len, tgt, target_map, progress_cb); - - fclose(src); - fclose(tgt); - - if (ret != 0) { - return ErrorAbort(state, "%s(): patching failed\n", name); - } - } else { - rewind(src); - if (CheckMappedFileSha1(src, target_map, target_digest)) { - // If the partition contents match the target already, we - // don't need to do anything. - printf("%s: output is already target\n", name); - } else { - return ErrorAbort(state, "%s(): %s in unknown state\n", name, filename); - } - } - - done: - free(target_sha1); - free(target_mapfilename); - free(init_sha1); - free(init_mapfilename); - free(patch_filename); - return StringValue(filename); - -} - // apply_patch(file, size, init_sha1, tgt_sha1, patch) Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { @@ -1738,7 +1538,6 @@ void RegisterInstallFunctions() { RegisterFunction("apply_patch_space", ApplyPatchSpaceFn); RegisterFunction("wipe_block_device", WipeBlockDeviceFn); - RegisterFunction("syspatch", SysPatchFn); RegisterFunction("read_file", ReadFileFn); RegisterFunction("sha1_check", Sha1CheckFn); -- cgit v1.2.3 From 8328922ff040280007da0aaaf8b567581231d5ed Mon Sep 17 00:00:00 2001 From: Andrew Boie Date: Wed, 3 Sep 2014 12:41:06 -0700 Subject: use lseek64 instead of lseek Otherwise, overflow problems can occur with images larger than 2G since the offsets will overflow a 32-bit off_t. Change-Id: I05951a38ebeae83ad2cb938594e8d8adb323e2aa Signed-off-by: Andrew Boie --- updater/blockimg.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'updater') diff --git a/updater/blockimg.c b/updater/blockimg.c index 6d412d5d5..5c0c3a727 100644 --- a/updater/blockimg.c +++ b/updater/blockimg.c @@ -106,12 +106,12 @@ static void writeblock(int fd, const uint8_t* data, size_t size) { } } -static void check_lseek(int fd, off_t offset, int whence) { +static void check_lseek(int fd, off64_t offset, int whence) { while (true) { - off_t ret = lseek(fd, offset, whence); + off64_t ret = lseek64(fd, offset, whence); if (ret < 0) { if (errno != EINTR) { - fprintf(stderr, "lseek failed: %s\n", strerror(errno)); + fprintf(stderr, "lseek64 failed: %s\n", strerror(errno)); exit(1); } } else { @@ -165,7 +165,7 @@ static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { ++rss->p_block; if (rss->p_block < rss->tgt->count) { rss->p_remain = (rss->tgt->pos[rss->p_block*2+1] - rss->tgt->pos[rss->p_block*2]) * BLOCKSIZE; - check_lseek(rss->fd, rss->tgt->pos[rss->p_block*2] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(rss->fd, (off64_t)rss->tgt->pos[rss->p_block*2] * BLOCKSIZE, SEEK_SET); } else { // we can't write any more; return how many bytes have // been written so far. @@ -414,7 +414,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); size_t p = 0; for (i = 0; i < src->count; ++i) { - check_lseek(fd, src->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET); size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; readblock(fd, buffer+p, sz); p += sz; @@ -422,7 +422,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] p = 0; for (i = 0; i < tgt->count; ++i) { - check_lseek(fd, tgt->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); size_t sz = (tgt->pos[i*2+1] - tgt->pos[i*2]) * BLOCKSIZE; writeblock(fd, buffer+p, sz); p += sz; @@ -445,7 +445,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] allocate(BLOCKSIZE, &buffer, &buffer_alloc); memset(buffer, 0, BLOCKSIZE); for (i = 0; i < tgt->count; ++i) { - check_lseek(fd, tgt->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(fd, (off64_t)tgt->pos[i*2] * BLOCKSIZE, SEEK_SET); for (j = tgt->pos[i*2]; j < tgt->pos[i*2+1]; ++j) { writeblock(fd, buffer, BLOCKSIZE); } @@ -470,7 +470,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] rss.tgt = tgt; rss.p_block = 0; rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; - check_lseek(fd, tgt->pos[0] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET); pthread_mutex_lock(&nti.mu); nti.rss = &rss; @@ -504,7 +504,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); size_t p = 0; for (i = 0; i < src->count; ++i) { - check_lseek(fd, src->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(fd, (off64_t)src->pos[i*2] * BLOCKSIZE, SEEK_SET); size_t sz = (src->pos[i*2+1] - src->pos[i*2]) * BLOCKSIZE; readblock(fd, buffer+p, sz); p += sz; @@ -520,7 +520,7 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] rss.tgt = tgt; rss.p_block = 0; rss.p_remain = (tgt->pos[1] - tgt->pos[0]) * BLOCKSIZE; - check_lseek(fd, tgt->pos[0] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET); if (style[0] == 'i') { // imgdiff ApplyImagePatch(buffer, src->size * BLOCKSIZE, @@ -620,7 +620,7 @@ Value* RangeSha1Fn(const char* name, State* state, int argc, Expr* argv[]) { int i, j; for (i = 0; i < rs->count; ++i) { - check_lseek(fd, rs->pos[i*2] * (off_t)BLOCKSIZE, SEEK_SET); + check_lseek(fd, (off64_t)rs->pos[i*2] * BLOCKSIZE, SEEK_SET); for (j = rs->pos[i*2]; j < rs->pos[i*2+1]; ++j) { readblock(fd, buffer, BLOCKSIZE); SHA_update(&ctx, buffer, BLOCKSIZE); -- cgit v1.2.3 From f7bb09dae8d7c89130648ef2aca7025860b6d801 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 4 Sep 2014 08:10:32 -0700 Subject: fix comment in blockimg updater code The comment for the DEBUG_ERASE setting is exactly backwards. Change-Id: I98ab5828365894217fc78976817a131e7d22d5c1 --- updater/blockimg.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'updater') diff --git a/updater/blockimg.c b/updater/blockimg.c index 6d412d5d5..555230dd3 100644 --- a/updater/blockimg.c +++ b/updater/blockimg.c @@ -37,9 +37,9 @@ #define BLOCKSIZE 4096 -// Set this to 1 to interpret 'erase' transfers to mean do a -// BLKDISCARD ioctl. Set to 0 to interpret erase to mean fill the -// region with zeroes. +// Set this to 0 to interpret 'erase' transfers to mean do a +// BLKDISCARD ioctl (the normal behavior). Set to 1 to interpret +// erase to mean fill the region with zeroes. #define DEBUG_ERASE 0 #ifndef BLKDISCARD -- cgit v1.2.3 From d5b1727765060b2886d11cf7af9588b818be12cf Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Wed, 22 Oct 2014 14:28:23 -0700 Subject: Treat already-renamed files as having no problems. This should help with reentrant OTAs. Bug: 18079773 Change-Id: I102fd738e3b450483ecd4471384c12e89fc586e2 --- updater/install.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index dad0d08c9..42dbb58da 100644 --- a/updater/install.c +++ b/updater/install.c @@ -358,6 +358,9 @@ Value* RenameFn(const char* name, State* state, int argc, Expr* argv[]) { if (make_parents(dst_name) != 0) { ErrorAbort(state, "Creating parent of %s failed, error %s", dst_name, strerror(errno)); + } else if (access(dst_name, F_OK) == 0 && access(src_name, F_OK) != 0) { + // File was already moved + result = dst_name; } else if (rename(src_name, dst_name) != 0) { ErrorAbort(state, "Rename of %s to %s failed, error %s", src_name, dst_name, strerror(errno)); -- cgit v1.2.3 From 7548025bdd111421cff2fe73839c01e2dc0a0365 Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Wed, 22 Oct 2014 19:48:41 -0700 Subject: Log to UI any metadata setting errors Bug: 18079773 Change-Id: Ic6fddbcbcb6ddb9e1cbd1698df98387c0033ae15 --- updater/install.c | 84 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 34 deletions(-) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index 42dbb58da..17ea4c2b5 100644 --- a/updater/install.c +++ b/updater/install.c @@ -52,6 +52,26 @@ #include "wipe.h" #endif +void uiPrint(State* state, char* buffer) { + char* line = strtok(buffer, "\n"); + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); + while (line) { + fprintf(ui->cmd_pipe, "ui_print %s\n", line); + line = strtok(NULL, "\n"); + } + fprintf(ui->cmd_pipe, "ui_print\n"); +} + +__attribute__((__format__(printf, 2, 3))) __nonnull((2)) +void uiPrintf(State* state, const char* format, ...) { + char error_msg[1024]; + va_list ap; + va_start(ap, format); + vsnprintf(error_msg, sizeof(error_msg), format, ap); + va_end(ap); + uiPrint(state, error_msg); +} + // Take a sha-1 digest and return it as a newly-allocated hex string. char* PrintSha1(const uint8_t* digest) { char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1); @@ -633,7 +653,7 @@ struct perm_parsed_args { uint64_t capabilities; }; -static struct perm_parsed_args ParsePermArgs(int argc, char** args) { +static struct perm_parsed_args ParsePermArgs(State * state, int argc, char** args) { int i; struct perm_parsed_args parsed; int bad = 0; @@ -648,7 +668,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { parsed.uid = uid; parsed.has_uid = true; } else { - printf("ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]); bad++; } continue; @@ -659,7 +679,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { parsed.gid = gid; parsed.has_gid = true; } else { - printf("ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]); bad++; } continue; @@ -670,7 +690,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { parsed.mode = mode; parsed.has_mode = true; } else { - printf("ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]); bad++; } continue; @@ -681,7 +701,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { parsed.dmode = mode; parsed.has_dmode = true; } else { - printf("ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]); bad++; } continue; @@ -692,7 +712,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { parsed.fmode = mode; parsed.has_fmode = true; } else { - printf("ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]); bad++; } continue; @@ -703,7 +723,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { parsed.capabilities = capabilities; parsed.has_capabilities = true; } else { - printf("ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]); bad++; } continue; @@ -713,7 +733,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { parsed.selabel = args[i+1]; parsed.has_selabel = true; } else { - printf("ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]); + uiPrintf(state, "ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]); bad++; } continue; @@ -730,6 +750,7 @@ static struct perm_parsed_args ParsePermArgs(int argc, char** args) { } static int ApplyParsedPerms( + State * state, const char* filename, const struct stat *statptr, struct perm_parsed_args parsed) @@ -743,39 +764,39 @@ static int ApplyParsedPerms( if (parsed.has_uid) { if (chown(filename, parsed.uid, -1) < 0) { - printf("ApplyParsedPerms: chown of %s to %d failed: %s\n", - filename, parsed.uid, strerror(errno)); + uiPrintf(state, "ApplyParsedPerms: chown of %s to %d failed: %s\n", + filename, parsed.uid, strerror(errno)); bad++; } } if (parsed.has_gid) { if (chown(filename, -1, parsed.gid) < 0) { - printf("ApplyParsedPerms: chgrp of %s to %d failed: %s\n", - filename, parsed.gid, strerror(errno)); + uiPrintf(state, "ApplyParsedPerms: chgrp of %s to %d failed: %s\n", + filename, parsed.gid, strerror(errno)); bad++; } } if (parsed.has_mode) { if (chmod(filename, parsed.mode) < 0) { - printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n", - filename, parsed.mode, strerror(errno)); + uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n", + filename, parsed.mode, strerror(errno)); bad++; } } if (parsed.has_dmode && S_ISDIR(statptr->st_mode)) { if (chmod(filename, parsed.dmode) < 0) { - printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n", - filename, parsed.dmode, strerror(errno)); + uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n", + filename, parsed.dmode, strerror(errno)); bad++; } } if (parsed.has_fmode && S_ISREG(statptr->st_mode)) { if (chmod(filename, parsed.fmode) < 0) { - printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n", + uiPrintf(state, "ApplyParsedPerms: chmod of %s to %d failed: %s\n", filename, parsed.fmode, strerror(errno)); bad++; } @@ -784,8 +805,8 @@ static int ApplyParsedPerms( if (parsed.has_selabel) { // TODO: Don't silently ignore ENOTSUP if (lsetfilecon(filename, parsed.selabel) && (errno != ENOTSUP)) { - printf("ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n", - filename, parsed.selabel, strerror(errno)); + uiPrintf(state, "ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n", + filename, parsed.selabel, strerror(errno)); bad++; } } @@ -794,7 +815,7 @@ static int ApplyParsedPerms( if (parsed.capabilities == 0) { if ((removexattr(filename, XATTR_NAME_CAPS) == -1) && (errno != ENODATA)) { // Report failure unless it's ENODATA (attribute not set) - printf("ApplyParsedPerms: removexattr of %s to %" PRIx64 " failed: %s\n", + uiPrintf(state, "ApplyParsedPerms: removexattr of %s to %" PRIx64 " failed: %s\n", filename, parsed.capabilities, strerror(errno)); bad++; } @@ -807,8 +828,8 @@ static int ApplyParsedPerms( cap_data.data[1].permitted = (uint32_t) (parsed.capabilities >> 32); cap_data.data[1].inheritable = 0; if (setxattr(filename, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) < 0) { - printf("ApplyParsedPerms: setcap of %s to %" PRIx64 " failed: %s\n", - filename, parsed.capabilities, strerror(errno)); + uiPrintf(state, "ApplyParsedPerms: setcap of %s to %" PRIx64 " failed: %s\n", + filename, parsed.capabilities, strerror(errno)); bad++; } } @@ -820,10 +841,11 @@ static int ApplyParsedPerms( // nftw doesn't allow us to pass along context, so we need to use // global variables. *sigh* static struct perm_parsed_args recursive_parsed_args; +static State* recursive_state; static int do_SetMetadataRecursive(const char* filename, const struct stat *statptr, int fileflags, struct FTW *pfwt) { - return ApplyParsedPerms(filename, statptr, recursive_parsed_args); + return ApplyParsedPerms(recursive_state, filename, statptr, recursive_parsed_args); } static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv[]) { @@ -848,14 +870,16 @@ static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv goto done; } - struct perm_parsed_args parsed = ParsePermArgs(argc, args); + struct perm_parsed_args parsed = ParsePermArgs(state, argc, args); if (recursive) { recursive_parsed_args = parsed; + recursive_state = state; bad += nftw(args[0], do_SetMetadataRecursive, 30, FTW_CHDIR | FTW_DEPTH | FTW_PHYS); memset(&recursive_parsed_args, 0, sizeof(recursive_parsed_args)); + recursive_state = NULL; } else { - bad += ApplyParsedPerms(args[0], &sb, parsed); + bad += ApplyParsedPerms(state, args[0], &sb, parsed); } done: @@ -1227,15 +1251,7 @@ Value* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) { } free(args); buffer[size] = '\0'; - - char* line = strtok(buffer, "\n"); - while (line) { - fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, - "ui_print %s\n", line); - line = strtok(NULL, "\n"); - } - fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "ui_print\n"); - + uiPrint(state, buffer); return StringValue(buffer); } -- cgit v1.2.3 From bd6138cffeedf1d60346ec9d34711fe38d0f8323 Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Wed, 22 Oct 2014 17:05:08 -0700 Subject: Allow passing of mount args to mountFn Bug: 18079773 Bug: 18092222 Change-Id: Ifc3f3e123de729dfbb2f49414b3207afa96268d5 --- updater/install.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index 17ea4c2b5..282a6188b 100644 --- a/updater/install.c +++ b/updater/install.c @@ -91,16 +91,27 @@ char* PrintSha1(const uint8_t* digest) { // fs_type="ext4" partition_type="EMMC" location=device Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; - if (argc != 4) { - return ErrorAbort(state, "%s() expects 4 args, got %d", name, argc); + if (argc != 4 && argc != 5) { + return ErrorAbort(state, "%s() expects 4-5 args, got %d", name, argc); } char* fs_type; char* partition_type; char* location; char* mount_point; - if (ReadArgs(state, argv, 4, &fs_type, &partition_type, + char* mount_options; + bool has_mount_options; + if (argc == 5) { + has_mount_options = true; + if (ReadArgs(state, argv, 5, &fs_type, &partition_type, + &location, &mount_point, &mount_options) < 0) { + return NULL; + } + } else { + has_mount_options = false; + if (ReadArgs(state, argv, 4, &fs_type, &partition_type, &location, &mount_point) < 0) { - return NULL; + return NULL; + } } if (strlen(fs_type) == 0) { @@ -154,7 +165,8 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { result = mount_point; } else { if (mount(location, mount_point, fs_type, - MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) { + MS_NOATIME | MS_NODEV | MS_NODIRATIME, + has_mount_options ? mount_options : "") < 0) { printf("%s: failed to mount %s at %s: %s\n", name, location, mount_point, strerror(errno)); result = strdup(""); @@ -168,6 +180,7 @@ done: free(partition_type); free(location); if (result != mount_point) free(mount_point); + if (has_mount_options) free(mount_options); return StringValue(result); } -- cgit v1.2.3 From 6a821fe5f054df674151d54e7b03be63afd35d58 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Thu, 23 Oct 2014 20:36:42 -0700 Subject: unconditionally apply SELinux labels to symlinks At the end of the OTA script, we walk through /system, updating all the permissions on the filesystem, including the UID, GID, standard UNIX permissions, capabilities, and SELinux labels. In the case of a symbolic link, however, we want to skip most of those operations. The UID, GID, UNIX permissions, and capabilities don't meaningfully apply to symbolic links. However, that's not true with SELinux labels. The SELinux label on a symbolic link is important. We need to make sure the label on the symbolic link is always updated, even if none of the other attributes are updated. This change unconditionally updates the SELinux label on the symbolic link itself. lsetfilecon() is used, so that the link itself is updated, not what it's pointing to. In addition, drop the ENOTSUP special case. SELinux has been a requirement since Android 4.4. Running without filesystem extended attributes is no longer supported, and we shouldn't even try to handle non-SELinux updates anymore. (Note: this could be problematic if these scripts are ever used to produce OTA images for 4.2 devices) Bug: 18079773 Change-Id: I87f99a1c88fe02bb2914f1884cac23ce1b385f91 --- updater/install.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index 282a6188b..db2bd3295 100644 --- a/updater/install.c +++ b/updater/install.c @@ -770,9 +770,17 @@ static int ApplyParsedPerms( { int bad = 0; + if (parsed.has_selabel) { + if (lsetfilecon(filename, parsed.selabel) != 0) { + uiPrintf(state, "ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n", + filename, parsed.selabel, strerror(errno)); + bad++; + } + } + /* ignore symlinks */ if (S_ISLNK(statptr->st_mode)) { - return 0; + return bad; } if (parsed.has_uid) { @@ -815,15 +823,6 @@ static int ApplyParsedPerms( } } - if (parsed.has_selabel) { - // TODO: Don't silently ignore ENOTSUP - if (lsetfilecon(filename, parsed.selabel) && (errno != ENOTSUP)) { - uiPrintf(state, "ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n", - filename, parsed.selabel, strerror(errno)); - bad++; - } - } - if (parsed.has_capabilities && S_ISREG(statptr->st_mode)) { if (parsed.capabilities == 0) { if ((removexattr(filename, XATTR_NAME_CAPS) == -1) && (errno != ENODATA)) { -- cgit v1.2.3 From f15e31edf948d9dac11858fba6dfdff8a5cd91c9 Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Fri, 24 Oct 2014 14:14:41 -0700 Subject: Log mount/unmount errors to UI Bug: 18092022 Change-Id: I6c42038ebeb1cfc1e7ca0d3e12310fdce1b990b0 --- updater/install.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'updater') diff --git a/updater/install.c b/updater/install.c index db2bd3295..ff7de4793 100644 --- a/updater/install.c +++ b/updater/install.c @@ -151,13 +151,13 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { const MtdPartition* mtd; mtd = mtd_find_partition_by_name(location); if (mtd == NULL) { - printf("%s: no mtd partition named \"%s\"", + uiPrintf(state, "%s: no mtd partition named \"%s\"", name, location); result = strdup(""); goto done; } if (mtd_mount_partition(mtd, mount_point, fs_type, 0 /* rw */) != 0) { - printf("mtd mount of %s failed: %s\n", + uiPrintf(state, "mtd mount of %s failed: %s\n", location, strerror(errno)); result = strdup(""); goto done; @@ -167,7 +167,7 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { if (mount(location, mount_point, fs_type, MS_NOATIME | MS_NODEV | MS_NODIRATIME, has_mount_options ? mount_options : "") < 0) { - printf("%s: failed to mount %s at %s: %s\n", + uiPrintf(state, "%s: failed to mount %s at %s: %s\n", name, location, mount_point, strerror(errno)); result = strdup(""); } else { @@ -231,10 +231,14 @@ Value* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) { scan_mounted_volumes(); const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point); if (vol == NULL) { - printf("unmount of %s failed; no such volume\n", mount_point); + uiPrintf(state, "unmount of %s failed; no such volume\n", mount_point); result = strdup(""); } else { - unmount_mounted_volume(vol); + int ret = unmount_mounted_volume(vol); + if (ret != 0) { + uiPrintf(state, "unmount of %s failed (%d): %s\n", + mount_point, ret, strerror(errno)); + } result = mount_point; } -- cgit v1.2.3