From 956cde8578c40ec812a933a14cb4e82f2c0de320 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Tue, 26 Jun 2012 15:01:03 -0700 Subject: Add mode when open(O_CREAT) is used. When creating a new file using open(..., O_CREAT), it is an error to fail to specify a creation mode. If a mode is not specified, a random stack provided value is used as the "mode". This will become a compile error in a future Android change. Change-Id: I73c1e1a39ca36bf01704b07302af4971d234b5a8 --- applypatch/applypatch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'applypatch/applypatch.c') diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 00004e9a8..488fd8c6f 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -324,7 +324,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); + int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (fd < 0) { printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno)); @@ -843,7 +843,7 @@ static int GenerateTarget(FileContents* source_file, strcpy(outname, target_filename); strcat(outname, ".patch"); - output = open(outname, O_WRONLY | O_CREAT | O_TRUNC); + output = open(outname, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (output < 0) { printf("failed to open output file %s: %s\n", outname, strerror(errno)); -- cgit v1.2.3 From a3ccba6d314cb29b02d1dbda9a71427b11da936d Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 20 Aug 2012 15:28:02 -0700 Subject: add bonus data feature to imgdiff/imgpatch/applypatch The bonus data option lets you give an additional blob of uncompressed data to be used when constructing a patch for chunk #1 of an image. The same blob must be available at patch time, and can be passed to the command-line applypatch tool (this feature is not accessible from edify scripts). This will be used to reduce the size of recovery-from-boot patches by storing parts of the recovery ramdisk (the UI images) on the system partition. Change-Id: Iac1959cdf7f5e4582f8d434e83456e483b64c02c --- applypatch/applypatch.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'applypatch/applypatch.c') diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 488fd8c6f..7b8a010e3 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -39,7 +39,8 @@ static int GenerateTarget(FileContents* source_file, const char* source_filename, const char* target_filename, const uint8_t target_sha1[SHA_DIGEST_SIZE], - size_t target_size); + size_t target_size, + const Value* bonus_data); static int mtd_partitions_scanned = 0; @@ -617,7 +618,8 @@ int applypatch(const char* source_filename, size_t target_size, int num_patches, char** const patch_sha1_str, - Value** patch_data) { + Value** patch_data, + Value* bonus_data) { printf("\napplying patch to %s\n", source_filename); if (target_filename[0] == '-' && @@ -699,7 +701,7 @@ int applypatch(const char* source_filename, int result = GenerateTarget(&source_file, source_patch_value, ©_file, copy_patch_value, source_filename, target_filename, - target_sha1, target_size); + target_sha1, target_size, bonus_data); free(source_file.data); free(copy_file.data); @@ -713,7 +715,8 @@ static int GenerateTarget(FileContents* source_file, const char* source_filename, const char* target_filename, const uint8_t target_sha1[SHA_DIGEST_SIZE], - size_t target_size) { + size_t target_size, + const Value* bonus_data) { int retry = 1; SHA_CTX ctx; int output; @@ -867,7 +870,7 @@ static int GenerateTarget(FileContents* source_file, } else if (header_bytes_read >= 8 && memcmp(header, "IMGDIFF2", 8) == 0) { result = ApplyImagePatch(source_to_use->data, source_to_use->size, - patch, sink, token, &ctx); + patch, sink, token, &ctx, bonus_data); } else { printf("Unknown patch file format\n"); return 1; -- cgit v1.2.3 From bf80f49edcec6b22ad7b1219e6ed6eda1e930c8c Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 19 Oct 2012 12:24:26 -0700 Subject: reduce some recovery logging Make minzip log only a count of files when extracting, not individual filenames. Make patching only chatter about free space if there's not enough and compact the other messages. Only the last 8k of the recovery log gets uploaded; this makes it more likely that we will get all of it. Change-Id: I529cb4947fe2185df82b9da5fae450a7480dcecd --- applypatch/applypatch.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'applypatch/applypatch.c') diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 7b8a010e3..69f8633ab 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -585,6 +585,14 @@ int CacheSizeCheck(size_t bytes) { } } +static void print_short_sha1(const uint8_t sha1[SHA_DIGEST_SIZE]) { + int i; + const char* hex = "0123456789abcdef"; + for (i = 0; i < 4; ++i) { + putchar(hex[(sha1[i]>>4) & 0xf]); + putchar(hex[sha1[i] & 0xf]); + } +} // This function applies binary patches to files in a way that is safe // (the original file is not touched until we have the desired @@ -620,7 +628,7 @@ int applypatch(const char* source_filename, char** const patch_sha1_str, Value** patch_data, Value* bonus_data) { - printf("\napplying patch to %s\n", source_filename); + printf("patch %s: ", source_filename); if (target_filename[0] == '-' && target_filename[1] == '\0') { @@ -646,8 +654,9 @@ int applypatch(const char* source_filename, if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) { // The early-exit case: the patch was already applied, this file // has the desired hash, nothing for us to do. - printf("\"%s\" is already target; no patch needed\n", - target_filename); + printf("already "); + print_short_sha1(target_sha1); + putchar('\n'); free(source_file.data); return 0; } @@ -769,8 +778,10 @@ static int GenerateTarget(FileContents* source_file, enough_space = (free_space > (256 << 10)) && // 256k (two-block) minimum (free_space > (target_size * 3 / 2)); // 50% margin of error - printf("target %ld bytes; free space %ld bytes; retry %d; enough %d\n", - (long)target_size, (long)free_space, retry, enough_space); + if (!enough_space) { + printf("target %ld bytes; free space %ld bytes; retry %d; enough %d\n", + (long)target_size, (long)free_space, retry, enough_space); + } } if (!enough_space) { @@ -805,7 +816,7 @@ static int GenerateTarget(FileContents* source_file, unlink(source_filename); size_t free_space = FreeSpaceForFile(target_fs); - printf("(now %ld bytes free for target)\n", (long)free_space); + printf("(now %ld bytes free for target) ", (long)free_space); } } @@ -901,6 +912,10 @@ static int GenerateTarget(FileContents* source_file, if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) { printf("patch did not produce expected sha1\n"); return 1; + } else { + printf("now "); + print_short_sha1(target_sha1); + putchar('\n'); } if (output < 0) { -- cgit v1.2.3 From 044a0b4d49a11edfa13471ce20914b0514eb7e0e Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 8 Jul 2013 09:42:54 -0700 Subject: recovery: try to write EMMC partitions more reliably Nexus 4 has flash errors that manifest during large writes (eg, of the radio partition). Writes of some blocks seem to be dropped silently, without any errors being returned to the user level. Make two changes to the partition-writing code: - break it up into 1MB writes instead of writing partitions with a single fwrite() call. Pause for 50ms in between every chunk. - read the partition back after writing and verify that we read what we wrote. Drop caches before reading so we (hopefully) are reading off the actual flash and not some cache. Neither of these should be necessary. Bug: 9602014 Change-Id: Ice2e24dd4c11f1a57968277b5eb1468c772f6f63 --- applypatch/applypatch.c | 78 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 6 deletions(-) (limited to 'applypatch/applypatch.c') diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 69f8633ab..2f134df05 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -421,18 +421,84 @@ int WriteToPartition(unsigned char* data, size_t len, break; case EMMC: - ; - FILE* f = fopen(partition, "wb"); - if (fwrite(data, 1, len, f) != len) { - printf("short write writing to %s (%s)\n", - partition, strerror(errno)); + { + size_t start = 0; + int success = 0; + FILE* f = fopen(partition, "r+b"); + if (f == NULL) { + printf("failed to open %s: %s\n", partition, strerror(errno)); return -1; } + int attempt; + + for (attempt = 0; attempt < 3; ++attempt) { + printf("write %s attempt %d start at %d\n", partition, attempt+1, start); + fseek(f, start, SEEK_SET); + while (start < len) { + size_t to_write = len - start; + if (to_write > (1<<20)) to_write = 1<<20; + + if (fwrite(data+start, 1, to_write, f) != to_write) { + printf("short write writing to %s (%s)\n", + partition, strerror(errno)); + return -1; + } + start += to_write; + if (start < len) { + usleep(50000); // 50 ms + } + } + + // drop caches so our subsequent verification read + // won't just be reading the cache. + sync(); + FILE* dc = fopen("/proc/sys/vm/drop_caches", "w"); + fwrite("3\n", 2, 1, dc); + fclose(dc); + sleep(1); + printf(" caches dropped\n"); + + // verify + fseek(f, 0, SEEK_SET); + unsigned char buffer[4096]; + start = len; + size_t p; + for (p = 0; p < len; p += sizeof(buffer)) { + size_t to_read = len - p; + if (to_read > sizeof(buffer)) to_read = sizeof(buffer); + + if (fread(buffer, 1, to_read, f) != to_read) { + printf("short verify read %s at %d: %s\n", + partition, p, strerror(errno)); + start = p; + break; + } + + if (memcmp(buffer, data+p, to_read)) { + printf("verification failed starting at %d\n", p); + start = p; + break; + } + } + + if (start == len) { + printf("verification read succeeded (attempt %d)\n", attempt+1); + success = true; + break; + } + } + + if (!success) { + printf("failed to verify after all attempts\n"); + return -1; + } + if (fclose(f) != 0) { printf("error closing %s (%s)\n", partition, strerror(errno)); return -1; } break; + } } free(copy); @@ -473,7 +539,7 @@ int ParseSha1(const char* str, uint8_t* digest) { // Search an array of sha1 strings for one matching the given sha1. // Return the index of the match on success, or -1 if no match is // found. -int FindMatchingPatch(uint8_t* sha1, const char** patch_sha1_str, +int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str, int num_patches) { int i; uint8_t patch_sha1[SHA_DIGEST_SIZE]; -- cgit v1.2.3 From c870a99c4aeb9e232ee68951e666b5fa670d1680 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Tue, 9 Jul 2013 10:34:46 -0700 Subject: recovery: write partitions more conservatively Write and verify partitions using write(2) and read(2) rather than the stdio functions. Read and write in 4kb blocks. When writing, fsync() every 1MB. Bug: 9602014 Change-Id: Ie98ce38e857786fc0f4ebf36bb5ffc93b41bc96f --- applypatch/applypatch.c | 70 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 24 deletions(-) (limited to 'applypatch/applypatch.c') diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 2f134df05..3f67c32df 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -424,42 +424,50 @@ int WriteToPartition(unsigned char* data, size_t len, { size_t start = 0; int success = 0; - FILE* f = fopen(partition, "r+b"); - if (f == NULL) { + int fd = open(partition, O_RDWR); + if (fd < 0) { printf("failed to open %s: %s\n", partition, strerror(errno)); return -1; } int attempt; - for (attempt = 0; attempt < 3; ++attempt) { - printf("write %s attempt %d start at %d\n", partition, attempt+1, start); - fseek(f, start, SEEK_SET); + for (attempt = 0; attempt < 10; ++attempt) { + off_t next_sync = start + (1<<20); + printf("raw write %s attempt %d start at %d\n", partition, attempt+1, start); + lseek(fd, start, SEEK_SET); while (start < len) { size_t to_write = len - start; - if (to_write > (1<<20)) to_write = 1<<20; - - if (fwrite(data+start, 1, to_write, f) != to_write) { - printf("short write writing to %s (%s)\n", - partition, strerror(errno)); - return -1; + if (to_write > 4096) to_write = 4096; + + ssize_t written = write(fd, data+start, to_write); + if (written < 0) { + if (errno == EINTR) { + written = 0; + } else { + printf("failed write writing to %s (%s)\n", + partition, strerror(errno)); + return -1; + } } - start += to_write; - if (start < len) { - usleep(50000); // 50 ms + start += written; + if (start >= next_sync) { + fsync(fd); + next_sync = start + (1<<20); } } + fsync(fd); // drop caches so our subsequent verification read // won't just be reading the cache. sync(); - FILE* dc = fopen("/proc/sys/vm/drop_caches", "w"); - fwrite("3\n", 2, 1, dc); - fclose(dc); + int dc = open("/proc/sys/vm/drop_caches", O_WRONLY); + write(dc, "3\n", 2); + close(dc); sleep(1); printf(" caches dropped\n"); // verify - fseek(f, 0, SEEK_SET); + lseek(fd, 0, SEEK_SET); unsigned char buffer[4096]; start = len; size_t p; @@ -467,11 +475,23 @@ int WriteToPartition(unsigned char* data, size_t len, size_t to_read = len - p; if (to_read > sizeof(buffer)) to_read = sizeof(buffer); - if (fread(buffer, 1, to_read, f) != to_read) { - printf("short verify read %s at %d: %s\n", - partition, p, strerror(errno)); - start = p; - break; + size_t so_far = 0; + while (so_far < to_read) { + ssize_t read_count = read(fd, buffer+so_far, to_read-so_far); + if (read_count < 0) { + if (errno == EINTR) { + read_count = 0; + } else { + printf("verify read error %s at %d: %s\n", + partition, p, strerror(errno)); + return -1; + } + } + if (read_count < to_read) { + printf("short verify read %s at %d: %d %d %s\n", + partition, p, read_count, to_read, strerror(errno)); + } + so_far += read_count; } if (memcmp(buffer, data+p, to_read)) { @@ -486,6 +506,8 @@ int WriteToPartition(unsigned char* data, size_t len, success = true; break; } + + sleep(2); } if (!success) { @@ -493,7 +515,7 @@ int WriteToPartition(unsigned char* data, size_t len, return -1; } - if (fclose(f) != 0) { + if (close(fd) != 0) { printf("error closing %s (%s)\n", partition, strerror(errno)); return -1; } -- cgit v1.2.3 From bf4a69ac41696fe78f6cc67b10cf1816186f1c5d Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 10 Jul 2013 13:39:50 -0700 Subject: recovery: sleep after writing partition and closing it Another speculative attempt to get everything we write actually stored to the device. Change-Id: Icf40b0741b4c535e55ea34848073a97d90dc0e70 --- applypatch/applypatch.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'applypatch/applypatch.c') diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 3f67c32df..2c62a0636 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -432,7 +432,7 @@ int WriteToPartition(unsigned char* data, size_t len, int attempt; for (attempt = 0; attempt < 10; ++attempt) { - off_t next_sync = start + (1<<20); + size_t next_sync = start + (1<<20); printf("raw write %s attempt %d start at %d\n", partition, attempt+1, start); lseek(fd, start, SEEK_SET); while (start < len) { @@ -487,7 +487,7 @@ int WriteToPartition(unsigned char* data, size_t len, return -1; } } - if (read_count < to_read) { + if ((size_t)read_count < to_read) { printf("short verify read %s at %d: %d %d %s\n", partition, p, read_count, to_read, strerror(errno)); } @@ -519,6 +519,11 @@ int WriteToPartition(unsigned char* data, size_t len, printf("error closing %s (%s)\n", partition, strerror(errno)); return -1; } + // hack: sync and sleep after closing in hopes of getting + // the data actually onto flash. + printf("sleeping after close\n"); + sync(); + sleep(5); break; } } -- cgit v1.2.3 From e8d953aa7ed0c16beb1b03a05d16cb23dd85e198 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 11 Jul 2013 12:09:05 -0700 Subject: recovery: more cargo-cult programming Add O_DIRECT|O_SYNC when opening partitions for write. Change-Id: I9825ad8e60fba87e482f8abc5593d6f54a1e3a1c --- applypatch/applypatch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'applypatch/applypatch.c') diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 2c62a0636..60e26ede3 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -424,7 +424,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_DIRECT | O_SYNC); if (fd < 0) { printf("failed to open %s: %s\n", partition, strerror(errno)); return -1; @@ -433,7 +433,7 @@ int WriteToPartition(unsigned char* data, size_t len, for (attempt = 0; attempt < 10; ++attempt) { size_t next_sync = start + (1<<20); - printf("raw write %s attempt %d start at %d\n", partition, attempt+1, start); + printf("raw O_DIRECT write %s attempt %d start at %d\n", partition, attempt+1, start); lseek(fd, start, SEEK_SET); while (start < len) { size_t to_write = len - start; -- cgit v1.2.3 From 901b898d5e4d7cc555974b8132f83f948f8fbaee Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 11 Jul 2013 12:31:25 -0700 Subject: recovery: remove O_DIRECT, use O_SYNC only O_DIRECT writes fail with EINVAL due to alignment issues. Change-Id: If8cf38a636313e4f4b4e61e66287dc903c473e5b --- applypatch/applypatch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'applypatch/applypatch.c') diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 60e26ede3..0dcdce0b1 100644 --- a/applypatch/applypatch.c +++ b/applypatch/applypatch.c @@ -424,7 +424,7 @@ int WriteToPartition(unsigned char* data, size_t len, { size_t start = 0; int success = 0; - int fd = open(partition, O_RDWR | O_DIRECT | O_SYNC); + 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 +433,7 @@ int WriteToPartition(unsigned char* data, size_t len, for (attempt = 0; attempt < 10; ++attempt) { size_t next_sync = start + (1<<20); - printf("raw O_DIRECT write %s attempt %d start at %d\n", partition, attempt+1, start); + printf("raw O_SYNC write %s attempt %d start at %d\n", partition, attempt+1, start); lseek(fd, start, SEEK_SET); while (start < len) { size_t to_write = len - start; -- cgit v1.2.3