From d83e4f15890ac6ebe0d61924bd224eb1ae8565ad Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 8 Sep 2014 12:22:09 -0700 Subject: support for version 2 of block image diffs In version 2 of block image diffs, we support a new command to load data from the image and store it in the "stash table" and then subsequently use entries in the stash table to fill in missing bits of source data we're not allowed to read when doing move/bsdiff/imgdiff commands. This leads to smaller update packages because we can break cycles in the ordering of how pieces are updated by storing data away and using it later, rather than not using the data as input to the patch system at all. This comes at the cost of the RAM or scratch disk needed to store the data. The implementation is backwards compatible; it can still handle the existing version 1 of the transfer file format. Change-Id: I4559bfd76d5403859637aeac832f3a5e9e13b63a --- updater/blockimg.c | 254 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 209 insertions(+), 45 deletions(-) diff --git a/updater/blockimg.c b/updater/blockimg.c index c3319c973..302689313 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 %lu bytes\n", + fprintf(stderr, "failed to allocate range of %zu bytes\n", sizeof(RangeSet) + num * sizeof(int)); exit(1); } @@ -245,6 +245,133 @@ static void* unzip_new_data(void* cookie) { return NULL; } +// Do a source/target load for move/bsdiff/imgdiff in version 1. +// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect +// to parse the remainder of the string as: +// +// +// +// The source range is loaded into the provided buffer, reallocating +// it to make it larger if necessary. The target ranges are returned +// in *tgt, if tgt is non-NULL. + +static void LoadSrcTgtVersion1(char* wordsave, RangeSet** tgt, int* src_blocks, + uint8_t** buffer, size_t* buffer_alloc, int fd) { + char* word; + + word = strtok_r(NULL, " ", &wordsave); + RangeSet* src = parse_range(word); + + if (tgt != NULL) { + word = strtok_r(NULL, " ", &wordsave); + *tgt = parse_range(word); + } + + allocate(src->size * BLOCKSIZE, buffer, buffer_alloc); + size_t p = 0; + int i; + for (i = 0; i < src->count; ++i) { + 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; + } + + *src_blocks = src->size; + free(src); +} + +static void MoveRange(uint8_t* dest, RangeSet* locs, const uint8_t* source) { + // source contains packed data, which we want to move to the + // locations given in *locs in the dest buffer. source and dest + // may be the same buffer. + + int start = locs->size; + int i; + for (i = locs->count-1; i >= 0; --i) { + int blocks = locs->pos[i*2+1] - locs->pos[i*2]; + start -= blocks; + memmove(dest + (locs->pos[i*2] * BLOCKSIZE), source + (start * BLOCKSIZE), + blocks * BLOCKSIZE); + } +} + +// Do a source/target load for move/bsdiff/imgdiff in version 2. +// 'wordsave' is the save_ptr of a strtok_r()-in-progress. We expect +// to parse the remainder of the string as one of: +// +// +// (loads data from source image only) +// +// - <[stash_id:stash_range] ...> +// (loads data from stashes only) +// +// <[stash_id:stash_range] ...> +// (loads data from both source image and stashes) +// +// On return, buffer is filled with the loaded source data (rearranged +// and combined with stashed data as necessary). buffer may be +// reallocated if needed to accommodate the source data. *tgt is the +// target RangeSet. Any stashes required are taken from stash_table +// and free()'d after being used. + +static void LoadSrcTgtVersion2(char* wordsave, RangeSet** tgt, int* src_blocks, + uint8_t** buffer, size_t* buffer_alloc, int fd, + uint8_t** stash_table) { + char* word; + + if (tgt != NULL) { + word = strtok_r(NULL, " ", &wordsave); + *tgt = parse_range(word); + } + + word = strtok_r(NULL, " ", &wordsave); + *src_blocks = strtol(word, NULL, 0); + + allocate(*src_blocks * BLOCKSIZE, buffer, buffer_alloc); + + word = strtok_r(NULL, " ", &wordsave); + if (word[0] == '-' && word[1] == '\0') { + // no source ranges, only stashes + } else { + RangeSet* src = parse_range(word); + + size_t p = 0; + int i; + for (i = 0; i < src->count; ++i) { + 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; + } + free(src); + + word = strtok_r(NULL, " ", &wordsave); + if (word == NULL) { + // no stashes, only source range + return; + } + + RangeSet* locs = parse_range(word); + MoveRange(*buffer, locs, *buffer); + } + + while ((word = strtok_r(NULL, " ", &wordsave)) != NULL) { + // Each word is a an index into the stash table, a colon, and + // then a rangeset describing where in the source block that + // stashed data should go. + char* colonsave = NULL; + char* colon = strtok_r(word, ":", &colonsave); + int stash_id = strtol(colon, NULL, 0); + colon = strtok_r(NULL, ":", &colonsave); + RangeSet* locs = parse_range(colon); + MoveRange(*buffer, locs, stash_table[stash_id]); + free(stash_table[stash_id]); + stash_table[stash_id] = NULL; + free(locs); + } +} + // args: // - block device (or file) to modify in-place // - transfer list (blob) @@ -311,23 +438,33 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] // 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 // + // move <...> + // bsdiff <...> + // imgdiff <...> + // - read the source blocks, apply a patch (or not in the + // case of move), write result to target blocks. bsdiff or + // imgdiff specifies the type of patch; move means no patch + // at all. + // + // The format of <...> differs between versions 1 and 2; + // see the LoadSrcTgtVersion{1,2}() functions for a + // description of what's expected. + // + // stash + // - (version 2 only) load the given source range and stash + // the data in the given slot of the stash table. + // // 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. // + // In version 2, the creator will guarantee that a given stash is + // loaded (with a stash command) before it's used in a + // move/bsdiff/imgdiff command. + // // 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. @@ -379,12 +516,18 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] line = strtok_r(transfer_list, "\n", &linesave); + int version; // first line in transfer list is the version number; currently // there's only version 1. - if (strcmp(line, "1") != 0) { + if (strcmp(line, "1") == 0) { + version = 1; + } else if (strcmp(line, "2") == 0) { + version = 2; + } else { ErrorAbort(state, "unexpected transfer list version [%s]\n", line); goto done; } + printf("blockimg version is %d\n", version); // second line in transfer list is the total number of blocks we // expect to write. @@ -394,33 +537,49 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] if (total_blocks == 0) ++total_blocks; int blocks_so_far = 0; + uint8_t** stash_table = NULL; + if (version >= 2) { + // Next line is how many stash entries are needed simultaneously. + line = strtok_r(NULL, "\n", &linesave); + int stash_entries = strtol(line, NULL, 0); + + stash_table = (uint8_t**) calloc(stash_entries, sizeof(uint8_t*)); + if (stash_table == NULL) { + fprintf(stderr, "failed to allocate %d-entry stash table\n", stash_entries); + exit(1); + } + + // Next line is the maximum number of blocks that will be + // stashed simultaneously. This could be used to verify that + // enough memory or scratch disk space is available. + line = strtok_r(NULL, "\n", &linesave); + int stash_max_blocks = strtol(line, NULL, 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); + RangeSet* tgt; + int src_blocks; + if (version == 1) { + LoadSrcTgtVersion1(wordsave, &tgt, &src_blocks, + &buffer, &buffer_alloc, fd); + } else if (version == 2) { + LoadSrcTgtVersion2(wordsave, &tgt, &src_blocks, + &buffer, &buffer_alloc, fd, stash_table); + } - printf(" moving %d blocks\n", src->size); + printf(" moving %d blocks\n", src_blocks); - allocate(src->size * BLOCKSIZE, &buffer, &buffer_alloc); size_t p = 0; - for (i = 0; i < src->count; ++i) { - 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; - } - - p = 0; for (i = 0; i < tgt->count; ++i) { 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; @@ -432,9 +591,20 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] fprintf(cmd_pipe, "set_progress %.4f\n", (double)blocks_so_far / total_blocks); fflush(cmd_pipe); - free(src); free(tgt); + } else if (strcmp("stash", style) == 0) { + word = strtok_r(NULL, " ", &wordsave); + int stash_id = strtol(word, NULL, 0); + int src_blocks; + size_t stash_alloc = 0; + + // Even though the "stash" style only appears in version + // 2, the version 1 source loader happens to do exactly + // what we want to read data into the stash_table. + LoadSrcTgtVersion1(wordsave, NULL, &src_blocks, + stash_table + stash_id, &stash_alloc, fd); + } else if (strcmp("zero", style) == 0 || (DEBUG_ERASE && strcmp("erase", style) == 0)) { word = strtok_r(NULL, " ", &wordsave); @@ -493,23 +663,18 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] 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, (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; + RangeSet* tgt; + int src_blocks; + if (version == 1) { + LoadSrcTgtVersion1(wordsave, &tgt, &src_blocks, + &buffer, &buffer_alloc, fd); + } else if (version == 2) { + LoadSrcTgtVersion2(wordsave, &tgt, &src_blocks, + &buffer, &buffer_alloc, fd, stash_table); } + printf(" patching %d blocks to %d\n", src_blocks, tgt->size); + Value patch_value; patch_value.type = VAL_BLOB; patch_value.size = patch_len; @@ -523,11 +688,11 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] check_lseek(fd, (off64_t)tgt->pos[0] * BLOCKSIZE, SEEK_SET); if (style[0] == 'i') { // imgdiff - ApplyImagePatch(buffer, src->size * BLOCKSIZE, + ApplyImagePatch(buffer, src_blocks * BLOCKSIZE, &patch_value, &RangeSinkWrite, &rss, NULL, NULL); } else { - ApplyBSDiffPatch(buffer, src->size * BLOCKSIZE, + ApplyBSDiffPatch(buffer, src_blocks * BLOCKSIZE, &patch_value, 0, &RangeSinkWrite, &rss, NULL); } @@ -541,7 +706,6 @@ Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[] 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; -- cgit v1.2.3 From 9eb8c8bdbe4965e9a92d83882e97cf67aee20821 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Mon, 20 Oct 2014 20:02:57 -0700 Subject: init.rc: Inidicate that booting is complete wrt firwmare requests ueventd will wait for /dev/.booting to go away before giving up on loading firmware. The issue was introduced in Ifdd5dd1e95d7e064dde5c80b70198882d949a710 which forgot to update recovery's init.rc Bug: 17993625 Change-Id: I91205fe6eea50aaef9b401d650ec8d6843a92a57 --- etc/init.rc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/etc/init.rc b/etc/init.rc index 1b402e20d..c654eedb8 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -45,7 +45,6 @@ on fs write /sys/class/android_usb/android0/iProduct ${ro.product.model} write /sys/class/android_usb/android0/iSerial ${ro.serialno} - on boot ifup lo hostname localhost @@ -57,6 +56,9 @@ on boot on load_all_props_action load_all_props +on firmware_mounts_complete + rm /dev/.booting + # Mount filesystems and start core system services. on late-init trigger early-fs @@ -69,6 +71,9 @@ on late-init # issued fs triggers have completed. trigger load_all_props_action + # Remove a file to wake up anything waiting for firmware + trigger firmware_mounts_complete + trigger early-boot trigger boot -- cgit v1.2.3 From 2f0ef73029fc51c6404121f338b034c8b516652c 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(+) 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 a9ad0324d1e351b1ef460972a871bcb03051cd6a Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Wed, 22 Oct 2014 18:38:48 -0700 Subject: Make /cache/recovery/last_log available in recovery Create a new recovery UI option to allow the user to view /cache/recovery/last_log for their device. This gives enhanced debugging information which may be necessary when a failed OTA occurs. Bug: 18094012 Change-Id: Ic3228de96e9bfc2a0141c7aab4ce392a38140cf3 --- default_device.cpp | 2 ++ device.h | 2 +- recovery.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 76 insertions(+), 5 deletions(-) diff --git a/default_device.cpp b/default_device.cpp index a25f05f8e..97806ac58 100644 --- a/default_device.cpp +++ b/default_device.cpp @@ -31,6 +31,7 @@ static const char* ITEMS[] = {"reboot system now", "wipe cache partition", "reboot to bootloader", "power down", + "view recovery logs", NULL }; class DefaultDevice : public Device { @@ -69,6 +70,7 @@ class DefaultDevice : public Device { case 3: return WIPE_CACHE; case 4: return REBOOT_BOOTLOADER; case 5: return SHUTDOWN; + case 6: return READ_RECOVERY_LASTLOG; default: return NO_ACTION; } } diff --git a/device.h b/device.h index 57ec3fc32..8ff4ec031 100644 --- a/device.h +++ b/device.h @@ -68,7 +68,7 @@ class Device { enum BuiltinAction { NO_ACTION, REBOOT, APPLY_EXT, APPLY_CACHE, // APPLY_CACHE is deprecated; has no effect APPLY_ADB_SIDELOAD, WIPE_DATA, WIPE_CACHE, - REBOOT_BOOTLOADER, SHUTDOWN }; + REBOOT_BOOTLOADER, SHUTDOWN, READ_RECOVERY_LASTLOG }; // Perform a recovery action selected from the menu. // 'menu_position' will be the item number of the selected menu diff --git a/recovery.cpp b/recovery.cpp index 7f17b16ef..e1a2a9678 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -77,6 +77,8 @@ static const char *SDCARD_ROOT = "/sdcard"; static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; +#define KEEP_LOG_COUNT 10 + RecoveryUI* ui = NULL; char* locale = NULL; char recovery_version[PROPERTY_VALUE_MAX+1]; @@ -161,6 +163,12 @@ fopen_path(const char *path, const char *mode) { return fp; } +static void redirect_stdio(const char* filename) { + // If these fail, there's not really anywhere to complain... + freopen(filename, "a", stdout); setbuf(stdout, NULL); + freopen(filename, "a", stderr); setbuf(stderr, NULL); +} + // close a file, log an error if the error indicator is set static void check_and_fclose(FILE *fp, const char *name) { @@ -665,6 +673,65 @@ wipe_data(int confirm, Device* device) { ui->Print("Data wipe complete.\n"); } +static void file_to_ui(const char* fn) { + FILE *fp = fopen_path(fn, "re"); + if (fp == NULL) { + ui->Print(" Unable to open %s: %s\n", fn, strerror(errno)); + return; + } + char line[1024]; + int ct = 0; + redirect_stdio("/dev/null"); + while(fgets(line, sizeof(line), fp) != NULL) { + ui->Print("%s", line); + ct++; + if (ct % 30 == 0) { + // give the user time to glance at the entries + ui->WaitKey(); + } + } + redirect_stdio(TEMPORARY_LOG_FILE); + fclose(fp); +} + +static void choose_recovery_file(Device* device) { + int i; + static const char** title_headers = NULL; + char *filename; + const char* headers[] = { "Select file to view", + "", + NULL }; + char* entries[KEEP_LOG_COUNT + 2]; + memset(entries, 0, sizeof(entries)); + + for (i = 0; i < KEEP_LOG_COUNT; i++) { + char *filename; + if (asprintf(&filename, (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), i) == -1) { + // memory allocation failure - return early. Should never happen. + return; + } + if ((ensure_path_mounted(filename) != 0) || (access(filename, R_OK) == -1)) { + free(filename); + entries[i+1] = NULL; + break; + } + entries[i+1] = filename; + } + + entries[0] = strdup("Go back"); + title_headers = prepend_title((const char**)headers); + + while(1) { + int chosen_item = get_menu_selection(title_headers, entries, 1, 0, device); + if (chosen_item == 0) break; + file_to_ui(entries[chosen_item]); + } + + for (i = 0; i < KEEP_LOG_COUNT + 1; i++) { + free(entries[i]); + } +} + // Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION // means to take the default, which is to reboot or shutdown depending // on if the --shutdown_after flag was passed to recovery. @@ -760,6 +827,10 @@ prompt_and_wait(Device* device, int status) { ui->Print("\nAPPLY_CACHE is deprecated.\n"); break; + case Device::READ_RECOVERY_LASTLOG: + choose_recovery_file(device); + break; + case Device::APPLY_ADB_SIDELOAD: status = apply_from_adb(ui, &wipe_cache, TEMPORARY_INSTALL_FILE); if (status >= 0) { @@ -824,9 +895,7 @@ int main(int argc, char **argv) { time_t start = time(NULL); - // If these fail, there's not really anywhere to complain... - freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL); - freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL); + redirect_stdio(TEMPORARY_LOG_FILE); // If this binary is started with the single argument "--adbd", // instead of being the normal recovery binary, it turns into kind @@ -844,7 +913,7 @@ main(int argc, char **argv) { load_volume_table(); ensure_path_mounted(LAST_LOG_FILE); - rotate_last_logs(10); + rotate_last_logs(KEEP_LOG_COUNT); get_args(&argc, &argv); const char *send_intent = NULL; -- cgit v1.2.3 From d4a63426ef565f35cef48e8bf7e69435642182b2 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(-) diff --git a/updater/install.c b/updater/install.c index dad0d08c9..95c8c1111 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); @@ -630,7 +650,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; @@ -645,7 +665,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; @@ -656,7 +676,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; @@ -667,7 +687,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; @@ -678,7 +698,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; @@ -689,7 +709,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; @@ -700,7 +720,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; @@ -710,7 +730,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; @@ -727,6 +747,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) @@ -740,39 +761,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++; } @@ -781,8 +802,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++; } } @@ -791,7 +812,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++; } @@ -804,8 +825,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++; } } @@ -817,10 +838,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[]) { @@ -845,14 +867,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: @@ -1224,15 +1248,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 168f77787700f0e9f66675beef33c593a777e64e 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(-) 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 688024169df70336cc128ea8cc929174c53a501e 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(-) 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 5ddf4293df45a051c7900eeb62fb5ec4950b6cb6 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(-) 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 From 042c3cd04ec3ef416019fa382cd5b1a942bd79e1 Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Tue, 28 Oct 2014 19:49:57 -0700 Subject: Force sync files written by minzip. Some files appear to be missing their sync to disk. Bug: 18145574 Change-Id: Ic858624a4dd65bbfc54d30f3a13c607078270345 --- minzip/Zip.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/minzip/Zip.c b/minzip/Zip.c index 5070104d3..70aff00cd 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -1067,7 +1067,8 @@ bool mzExtractRecursive(const ZipArchive *pArchive, setfscreatecon(secontext); } - int fd = creat(targetFile, UNZIP_FILEMODE); + int fd = open(targetFile, O_CREAT|O_WRONLY|O_TRUNC|O_SYNC + , UNZIP_FILEMODE); if (secontext) { freecon(secontext); @@ -1082,7 +1083,12 @@ bool mzExtractRecursive(const ZipArchive *pArchive, } bool ok = mzExtractZipEntryToFile(pArchive, pEntry, fd); - close(fd); + if (ok) { + ok = (fsync(fd) == 0); + } + if (close(fd) != 0) { + ok = false; + } if (!ok) { LOGE("Error extracting \"%s\"\n", targetFile); ok = false; -- cgit v1.2.3 From be81e51c171a0cf9b9b5045ee949282b7f36d779 Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Wed, 29 Oct 2014 12:42:15 -0700 Subject: Use more aggressive sync writing to applypatch. We have seen cases where the boot partition is patched, but upon recovery the partition appears to be corrupted. Open up all patched files/partitions with O_SYNC, and do not ignore the errors from fsync/close operations. Bug: 18170529 Change-Id: I392ad0a321d937c4ad02eaeea9170be384a4744b --- applypatch/applypatch.c | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index bfb9440e4..2c86e0984 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -309,7 +309,7 @@ static int LoadPartitionContents(const char* filename, FileContents* file) { // Save the contents of the given FileContents object under the given // filename. Return 0 on success. int SaveFileContents(const char* filename, const FileContents* file) { - int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR); if (fd < 0) { printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno)); @@ -324,8 +324,14 @@ int SaveFileContents(const char* filename, const FileContents* file) { close(fd); return -1; } - fsync(fd); - close(fd); + if (fsync(fd) != 0) { + printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno)); + return -1; + } + if (close(fd) != 0) { + printf("close of \"%s\" failed: %s\n", filename, strerror(errno)); + return -1; + } if (chmod(filename, file->st.st_mode) != 0) { printf("chmod of \"%s\" failed: %s\n", filename, strerror(errno)); @@ -408,7 +414,7 @@ int WriteToPartition(unsigned char* data, size_t len, { size_t start = 0; int success = 0; - int fd = open(partition, O_RDWR); + int fd = open(partition, O_RDWR | O_SYNC); if (fd < 0) { printf("failed to open %s: %s\n", partition, strerror(errno)); return -1; @@ -433,7 +439,22 @@ int WriteToPartition(unsigned char* data, size_t len, } start += written; } - fsync(fd); + if (fsync(fd) != 0) { + printf("failed to sync to %s (%s)\n", + partition, strerror(errno)); + return -1; + } + if (close(fd) != 0) { + printf("failed to close %s (%s)\n", + partition, strerror(errno)); + return -1; + } + fd = open(partition, O_RDONLY); + if (fd < 0) { + printf("failed to reopen %s for verify (%s)\n", + partition, strerror(errno)); + return -1; + } // drop caches so our subsequent verification read // won't just be reading the cache. @@ -919,7 +940,8 @@ static int GenerateTarget(FileContents* source_file, strcpy(outname, target_filename); strcat(outname, ".patch"); - output = open(outname, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + output = open(outname, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, + S_IRUSR | S_IWUSR); if (output < 0) { printf("failed to open output file %s: %s\n", outname, strerror(errno)); @@ -950,8 +972,14 @@ static int GenerateTarget(FileContents* source_file, } if (output >= 0) { - fsync(output); - close(output); + if (fsync(output) != 0) { + printf("failed to fsync file \"%s\" (%s)\n", outname, strerror(errno)); + result = 1; + } + if (close(output) != 0) { + printf("failed to close file \"%s\" (%s)\n", outname, strerror(errno)); + result = 1; + } } if (result != 0) { -- cgit v1.2.3 From e5879c3639789d61803605c12371a4f291e0b3cc Mon Sep 17 00:00:00 2001 From: Heather Lee Wilson Date: Fri, 14 Nov 2014 09:57:54 -0800 Subject: Byte swap to support BGRA in recovery mode Fixes color palate of recovery mode when double buffer enabled. Bug: 18169447 Change-Id: Ia50b0fb9afd8001dfd740c09ce109fa421e691f7 --- minui/graphics_fbdev.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/minui/graphics_fbdev.c b/minui/graphics_fbdev.c index c0c1bcb1a..6df2726f5 100644 --- a/minui/graphics_fbdev.c +++ b/minui/graphics_fbdev.c @@ -179,6 +179,18 @@ static gr_surface fbdev_init(minui_backend* backend) { static gr_surface fbdev_flip(minui_backend* backend __unused) { if (double_buffered) { +#if defined(RECOVERY_BGRA) + // In case of BGRA, do some byte swapping + unsigned int idx; + unsigned char tmp; + unsigned char* ucfb_vaddr = (unsigned char*)gr_draw->data; + for (idx = 0 ; idx < (gr_draw->height * gr_draw->row_bytes); + idx += 4) { + tmp = ucfb_vaddr[idx]; + ucfb_vaddr[idx ] = ucfb_vaddr[idx + 2]; + ucfb_vaddr[idx + 2] = tmp; + } +#endif // Change gr_draw to point to the buffer currently displayed, // then flip the driver so we're displaying the other buffer // instead. -- cgit v1.2.3 From b278c252e148798346f85fc92eeea6afeb33fbf0 Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Fri, 21 Nov 2014 00:12:28 -0800 Subject: Add support for tune2fs file operations This allows tune2fs to be executed from within OTA scripts, allowing for file system modifications without formatting the partition Bug: 18430740 Change-Id: I0c2e05b5ef4a81ecea043e9b7b99b545d18fe5e6 --- updater/Android.mk | 10 ++++++++++ updater/install.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/updater/Android.mk b/updater/Android.mk index a3a900a80..11e7bb807 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -34,6 +34,16 @@ LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz LOCAL_STATIC_LIBRARIES += libmincrypt libbz LOCAL_STATIC_LIBRARIES += libcutils liblog libstdc++ libc LOCAL_STATIC_LIBRARIES += libselinux +tune2fs_static_libraries := \ + libext2_com_err \ + libext2_blkid \ + libext2_quota \ + libext2_uuid_static \ + libext2_e2p \ + libext2fs +LOCAL_STATIC_LIBRARIES += libtune2fs $(tune2fs_static_libraries) + +LOCAL_C_INCLUDES += external/e2fsprogs/misc LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. # Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function diff --git a/updater/install.c b/updater/install.c index ff7de4793..2b2ffb0c5 100644 --- a/updater/install.c +++ b/updater/install.c @@ -46,6 +46,7 @@ #include "mtdutils/mtdutils.h" #include "updater.h" #include "install.h" +#include "tune2fs.h" #ifdef USE_EXT4 #include "make_ext4fs.h" @@ -1539,6 +1540,37 @@ Value* EnableRebootFn(const char* name, State* state, int argc, Expr* argv[]) { return StringValue(strdup("t")); } +Value* Tune2FsFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc == 0) { + return ErrorAbort(state, "%s() expects args, got %d", name, argc); + } + + char** args = ReadVarArgs(state, argc, argv); + if (args == NULL) { + return ErrorAbort(state, "%s() could not read args", name); + } + + int i; + char** args2 = malloc(sizeof(char*) * (argc+1)); + // Tune2fs expects the program name as its args[0] + args2[0] = strdup(name); + for (i = 0; i < argc; ++i) { + args2[i + 1] = args[i]; + } + int result = tune2fs_main(argc + 1, args2); + for (i = 0; i < argc; ++i) { + free(args[i]); + } + free(args); + + free(args2[0]); + free(args2); + if (result != 0) { + return ErrorAbort(state, "%s() returned error code %d", name, result); + } + return StringValue(strdup("t")); +} + void RegisterInstallFunctions() { RegisterFunction("mount", MountFn); RegisterFunction("is_mounted", IsMountedFn); @@ -1589,4 +1621,5 @@ void RegisterInstallFunctions() { RegisterFunction("set_stage", SetStageFn); RegisterFunction("enable_reboot", EnableRebootFn); + RegisterFunction("tune2fs", Tune2FsFn); } -- cgit v1.2.3 From 4b54239173f685a558a65e53c01b461c65006e5c Mon Sep 17 00:00:00 2001 From: Michael Runge Date: Fri, 21 Nov 2014 16:00:45 -0800 Subject: Force write to disk while doing uncrypt This should reduce errors if the device reboots before the blocks are commited to disk. Bug: 18481902 Change-Id: I13cda1c78955e4c83522fbcf87ddb16cc9f97683 --- uncrypt/uncrypt.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c index 189fa57e1..361c80483 100644 --- a/uncrypt/uncrypt.c +++ b/uncrypt/uncrypt.c @@ -164,7 +164,8 @@ char* parse_recovery_command_file() if (f == NULL) { return NULL; } - FILE* fo = fopen(RECOVERY_COMMAND_FILE_TMP, "w"); + int fd = open(RECOVERY_COMMAND_FILE_TMP, O_WRONLY | O_SYNC); + FILE* fo = fdopen(fd, "w"); while (fgets(temp, sizeof(temp), f)) { printf("read: %s", temp); @@ -175,6 +176,7 @@ char* parse_recovery_command_file() fputs(temp, fo); } fclose(f); + fsync(fd); fclose(fo); if (fn) { @@ -190,7 +192,8 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de struct stat sb; int ret; - FILE* mapf = fopen(map_file, "w"); + int mapfd = open(map_file, O_WRONLY | O_SYNC); + FILE* mapf = fdopen(mapfd, "w"); ret = stat(path, &sb); if (ret != 0) { @@ -232,7 +235,7 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de int wfd = -1; if (encrypted) { - wfd = open(blk_dev, O_WRONLY); + wfd = open(blk_dev, O_WRONLY | O_SYNC); if (wfd < 0) { ALOGE("failed to open fd for writing: %s\n", strerror(errno)); return -1; @@ -302,9 +305,11 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de fprintf(mapf, "%d %d\n", ranges[i*2], ranges[i*2+1]); } + fsync(mapfd); fclose(mapf); close(fd); if (encrypted) { + fsync(wfd); close(wfd); } @@ -318,7 +323,7 @@ void wipe_misc() { struct fstab_rec* v = &fstab->recs[i]; if (!v->mount_point) continue; if (strcmp(v->mount_point, "/misc") == 0) { - int fd = open(v->blk_device, O_WRONLY); + int fd = open(v->blk_device, O_WRONLY | O_SYNC); uint8_t zeroes[1088]; // sizeof(bootloader_message) from recovery memset(zeroes, 0, sizeof(zeroes)); @@ -333,7 +338,7 @@ void wipe_misc() { written += w; } } - + fsync(fd); close(fd); } } -- cgit v1.2.3 From cd055ee72a5efaf4bcbc0f81692410d3ffcda5e0 Mon Sep 17 00:00:00 2001 From: Patrick Tjin Date: Tue, 9 Dec 2014 11:26:40 -0800 Subject: Save kernel logs to /cache/recovery Bug: 18642766 Change-Id: I6c8b7d8f9ffb688d3afdfe0d47c4142e711e421d Signed-off-by: Patrick Tjin --- recovery.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index e1a2a9678..176ac7a07 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +77,8 @@ static const char *CACHE_ROOT = "/cache"; static const char *SDCARD_ROOT = "/sdcard"; static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; +static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg"; +#define KLOG_DEFAULT_LEN (64 * 1024) #define KEEP_LOG_COUNT 10 @@ -259,6 +262,44 @@ set_sdcard_update_bootloader_message() { set_bootloader_message(&boot); } +// read from kernel log into buffer and write out to file +static void +save_kernel_log(const char *destination) { + int n; + char *buffer; + int klog_buf_len; + FILE *log; + + klog_buf_len = klogctl(KLOG_SIZE_BUFFER, 0, 0); + if (klog_buf_len <= 0) { + LOGE("Error getting klog size (%s), using default\n", strerror(errno)); + klog_buf_len = KLOG_DEFAULT_LEN; + } + + buffer = (char *)malloc(klog_buf_len); + if (!buffer) { + LOGE("Can't alloc %d bytes for klog buffer\n", klog_buf_len); + return; + } + + n = klogctl(KLOG_READ_ALL, buffer, klog_buf_len); + if (n < 0) { + LOGE("Error in reading klog (%s)\n", strerror(errno)); + free(buffer); + return; + } + + log = fopen_path(destination, "w"); + if (log == NULL) { + LOGE("Can't open %s\n", destination); + free(buffer); + return; + } + fwrite(buffer, n, 1, log); + check_and_fclose(log, destination); + free(buffer); +} + // How much of the temp log we have copied to the copy in cache. static long tmplog_offset = 0; @@ -306,8 +347,11 @@ copy_logs() { copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true); copy_log_file(TEMPORARY_LOG_FILE, LAST_LOG_FILE, false); copy_log_file(TEMPORARY_INSTALL_FILE, LAST_INSTALL_FILE, false); + save_kernel_log(LAST_KMSG_FILE); chmod(LOG_FILE, 0600); chown(LOG_FILE, 1000, 1000); // system user + chmod(LAST_KMSG_FILE, 0600); + chown(LAST_KMSG_FILE, 1000, 1000); // system user chmod(LAST_LOG_FILE, 0640); chmod(LAST_INSTALL_FILE, 0644); sync(); @@ -695,15 +739,26 @@ static void file_to_ui(const char* fn) { } static void choose_recovery_file(Device* device) { - int i; + unsigned int i; + unsigned int n; static const char** title_headers = NULL; char *filename; const char* headers[] = { "Select file to view", "", NULL }; - char* entries[KEEP_LOG_COUNT + 2]; + // "Go back" + LAST_KMSG_FILE + KEEP_LOG_COUNT + terminating NULL entry + char* entries[KEEP_LOG_COUNT + 3]; memset(entries, 0, sizeof(entries)); + n = 0; + entries[n++] = strdup("Go back"); + + // Add kernel kmsg file if available + if ((ensure_path_mounted(LAST_KMSG_FILE) == 0) && (access(LAST_KMSG_FILE, R_OK) == 0)) { + entries[n++] = strdup(LAST_KMSG_FILE); + } + + // Add LAST_LOG_FILE + LAST_LOG_FILE.x for (i = 0; i < KEEP_LOG_COUNT; i++) { char *filename; if (asprintf(&filename, (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), i) == -1) { @@ -712,13 +767,12 @@ static void choose_recovery_file(Device* device) { } if ((ensure_path_mounted(filename) != 0) || (access(filename, R_OK) == -1)) { free(filename); - entries[i+1] = NULL; + entries[n++] = NULL; break; } - entries[i+1] = filename; + entries[n++] = filename; } - entries[0] = strdup("Go back"); title_headers = prepend_title((const char**)headers); while(1) { @@ -727,7 +781,7 @@ static void choose_recovery_file(Device* device) { file_to_ui(entries[chosen_item]); } - for (i = 0; i < KEEP_LOG_COUNT + 1; i++) { + for (i = 0; i < (sizeof(entries) / sizeof(*entries)); i++) { free(entries[i]); } } -- cgit v1.2.3 From f59b994264b2e8ffe179e2405cda81811f8a9db1 Mon Sep 17 00:00:00 2001 From: Patrick Tjin Date: Tue, 9 Dec 2014 12:42:42 -0800 Subject: Add basic navigation controls to log viewer. Bug: 18642766 Change-Id: I95a6c8edf83513d421a041e79c15111b5c991dde Signed-off-by: Patrick Tjin --- recovery.cpp | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index 176ac7a07..1d22b248a 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -82,6 +82,9 @@ static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg"; #define KEEP_LOG_COUNT 10 +// Number of lines per page when displaying a file on screen +#define LINES_PER_PAGE 30 + RecoveryUI* ui = NULL; char* locale = NULL; char recovery_version[PROPERTY_VALUE_MAX+1]; @@ -725,15 +728,46 @@ static void file_to_ui(const char* fn) { } char line[1024]; int ct = 0; + int key = 0; redirect_stdio("/dev/null"); while(fgets(line, sizeof(line), fp) != NULL) { ui->Print("%s", line); ct++; - if (ct % 30 == 0) { + if (ct % LINES_PER_PAGE == 0) { // give the user time to glance at the entries - ui->WaitKey(); + key = ui->WaitKey(); + + if (key == KEY_POWER) { + break; + } + + if (key == KEY_VOLUMEUP) { + // Go back by seeking to the beginning and dumping ct - n + // lines. It's ugly, but this way we don't need to store + // the previous offsets. The files we're dumping here aren't + // expected to be very large. + int i; + + ct -= 2 * LINES_PER_PAGE; + if (ct < 0) { + ct = 0; + } + fseek(fp, 0, SEEK_SET); + for (i = 0; i < ct; i++) { + fgets(line, sizeof(line), fp); + } + ui->Print("^^^^^^^^^^\n"); + } } } + + // If the user didn't abort, then give the user time to glance at + // the end of the log, sorry, no rewind here + if (key != KEY_POWER) { + ui->Print("\n--END-- (press any key)\n"); + ui->WaitKey(); + } + redirect_stdio(TEMPORARY_LOG_FILE); fclose(fp); } -- cgit v1.2.3 From a72512cd058da7de4cdb667776ed47fad873f12c Mon Sep 17 00:00:00 2001 From: Sungmin Choi Date: Wed, 10 Dec 2014 21:57:09 +0900 Subject: Add O_CREAT option for open Factory reset fails if there is no file, for example, RECOVERY_COMMAND_FILE_TMP. So create file as adding O_CREAT option if it does not exist. error log: --------- beginning of crash 12-10 02:35:17.190 3059 3059 F libc : Fatal signal 11 (SIGSEGV), code 1, fault addr 0x30 in tid 3059 (uncrypt) 12-10 02:35:17.296 766 1528 W NativeCrashListener: Couldn't find ProcessRecord for pid 3059 12-10 02:35:17.296 191 191 I DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 12-10 02:35:17.296 191 191 E DEBUG : AM write failure (32 / Broken pipe) 12-10 02:35:17.296 191 191 I DEBUG : Build fingerprint: 'Android/aosp_hammerhead/hammerhead:5.1/LMP/hopemini12052127:userdebug/test-keys' 12-10 02:35:17.296 191 191 I DEBUG : Revision: '10' 12-10 02:35:17.297 191 191 I DEBUG : ABI: 'arm' 12-10 02:35:17.297 191 191 I DEBUG : pid: 3059, tid: 3059, name: uncrypt >>> /system/bin/uncrypt <<< 12-10 02:35:17.297 191 191 I DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x30 12-10 02:35:17.302 191 191 I DEBUG : r0 00000001 r1 be94b690 r2 fffffe90 r3 b6fdbf7c 12-10 02:35:17.302 191 191 I DEBUG : r4 00000000 r5 00000000 r6 b6fd8ca4 r7 be94b67c 12-10 02:35:17.302 191 191 I DEBUG : r8 00000000 r9 ffffffff sl b6ff582b fp be94b68d 12-10 02:35:17.302 191 191 I DEBUG : ip b6fcfd08 sp be94b648 lr b6f98fe5 pc b6f98fe4 cpsr 20070030 12-10 02:35:17.303 191 191 I DEBUG : 12-10 02:35:17.303 191 191 I DEBUG : backtrace: 12-10 02:35:17.303 191 191 I DEBUG : #00 pc 00032fe4 /system/lib/libc.so (fputs+29) 12-10 02:35:17.303 191 191 I DEBUG : #01 pc 000016a1 /system/bin/uncrypt 12-10 02:35:17.303 191 191 I DEBUG : #02 pc 0000114b /system/bin/uncrypt 12-10 02:35:17.303 191 191 I DEBUG : #03 pc 00012df5 /system/lib/libc.so (__libc_init+44) 12-10 02:35:17.303 191 191 I DEBUG : #04 pc 000013cc /system/bin/uncrypt 12-10 02:35:17.325 191 191 I DEBUG : 12-10 02:35:17.325 191 191 I DEBUG : Tombstone written to: /data/tombstones/tombstone_00 Bug: 18709330 Change-Id: Ib5dccdd366e829049938a188ea5f98d9e4e282db --- uncrypt/uncrypt.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c index 361c80483..7fb0989a7 100644 --- a/uncrypt/uncrypt.c +++ b/uncrypt/uncrypt.c @@ -164,7 +164,11 @@ char* parse_recovery_command_file() if (f == NULL) { return NULL; } - int fd = open(RECOVERY_COMMAND_FILE_TMP, O_WRONLY | O_SYNC); + int fd = open(RECOVERY_COMMAND_FILE_TMP, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR); + if (fd < 0) { + ALOGE("failed to open %s\n", RECOVERY_COMMAND_FILE_TMP); + return NULL; + } FILE* fo = fdopen(fd, "w"); while (fgets(temp, sizeof(temp), f)) { @@ -192,7 +196,11 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de struct stat sb; int ret; - int mapfd = open(map_file, O_WRONLY | O_SYNC); + int mapfd = open(map_file, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR); + if (mapfd < 0) { + ALOGE("failed to open %s\n", map_file); + return -1; + } FILE* mapf = fdopen(mapfd, "w"); ret = stat(path, &sb); -- cgit v1.2.3 From 5bf74b238b402eaaf8c5bd1663fe4d592e59421f Mon Sep 17 00:00:00 2001 From: Jesse Zhao Date: Thu, 8 Jan 2015 15:59:23 -0800 Subject: Bump up max_map_count value. Change-Id: Id3e2c0795b817db9a85bc84cba2aa05d20179d39 Bug: 18503789 --- etc/init.rc | 1 + 1 file changed, 1 insertion(+) diff --git a/etc/init.rc b/etc/init.rc index c654eedb8..c78a44a2a 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -30,6 +30,7 @@ on init chmod 0775 /tmp write /proc/sys/kernel/panic_on_oops 1 + write /proc/sys/vm/max_map_count 1000000 on fs mkdir /dev/usb-ffs 0770 shell shell -- cgit v1.2.3