diff options
Diffstat (limited to '')
50 files changed, 1339 insertions, 553 deletions
diff --git a/Android.mk b/Android.mk index 4a2238c3f..8d285c3c3 100644 --- a/Android.mk +++ b/Android.mk @@ -40,15 +40,27 @@ LOCAL_MODULE := recovery #LOCAL_FORCE_STATIC_EXECUTABLE := true RECOVERY_API_VERSION := 3 +RECOVERY_FSTAB_VERSION := 2 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) #LOCAL_STATIC_LIBRARIES := \ -# libext4_utils \ +# libext4_utils_static \ +# libsparse_static \ # libminzip \ +# libz \ # libmtdutils \ # libmincrypt \ # libminadbd \ -# libpixelflinger_static +# libminui \ +# libpixelflinger_static \ +# libpng \ +# libfs_mgr \ +# libcutils \ +# liblog \ +# libselinux \ +# libstdc++ \ +# libm \ +# libc LOCAL_C_INCLUDES += bionic external/stlport/stlport @@ -67,7 +79,7 @@ ifeq ($(TARGET_USERIMAGES_USE_EXT4), true) LOCAL_C_INCLUDES += system/extras/ext4_utils LOCAL_SHARED_LIBRARIES += libext4_utils endif -LOCAL_C_INCLUDES += external/libselinux/include + ifeq ($(HAVE_SELINUX), true) #LOCAL_C_INCLUDES += external/libselinux/include #LOCAL_STATIC_LIBRARIES += libselinux @@ -90,11 +102,11 @@ endif # TODO: Build the ramdisk image in a more principled way. LOCAL_MODULE_TAGS := eng -ifeq ($(TARGET_RECOVERY_UI_LIB),) +#ifeq ($(TARGET_RECOVERY_UI_LIB),) LOCAL_SRC_FILES += default_device.cpp -else - LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB) -endif +#else +# LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB) +#endif LOCAL_C_INCLUDES += system/extras/ext4_utils @@ -289,6 +301,9 @@ LOCAL_MODULE := libaosprecovery LOCAL_MODULE_TAGS := eng LOCAL_MODULES_TAGS = optional LOCAL_CFLAGS = +ifneq ($(wildcard system/core/libmincrypt/rsa_e_3.c),) + LOCAL_CFLAGS += -DHAS_EXPONENT +endif LOCAL_SRC_FILES = adb_install.cpp bootloader.cpp verifier.cpp mtdutils/mtdutils.c LOCAL_SHARED_LIBRARIES += libc liblog libcutils libmtdutils LOCAL_STATIC_LIBRARIES += libmincrypt diff --git a/CleanSpec.mk b/CleanSpec.mk index b84e1b65e..ecf89ae75 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -47,3 +47,4 @@ # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/recovery_intermediates) diff --git a/applypatch/applypatch.c b/applypatch/applypatch.c index 488fd8c6f..0dcdce0b1 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; @@ -420,18 +421,111 @@ 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; + int fd = open(partition, O_RDWR | O_SYNC); + if (fd < 0) { + printf("failed to open %s: %s\n", partition, strerror(errno)); + return -1; + } + int attempt; + + for (attempt = 0; attempt < 10; ++attempt) { + size_t next_sync = start + (1<<20); + 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; + 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 += 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(); + int dc = open("/proc/sys/vm/drop_caches", O_WRONLY); + write(dc, "3\n", 2); + close(dc); + sleep(1); + printf(" caches dropped\n"); + + // verify + lseek(fd, 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); + + 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 ((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)); + } + so_far += read_count; + } + + 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; + } + + sleep(2); + } + + if (!success) { + printf("failed to verify after all attempts\n"); return -1; } - if (fclose(f) != 0) { + + if (close(fd) != 0) { 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; + } } free(copy); @@ -472,7 +566,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]; @@ -584,6 +678,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 @@ -617,8 +719,9 @@ int applypatch(const char* source_filename, size_t target_size, int num_patches, char** const patch_sha1_str, - Value** patch_data) { - printf("\napplying patch to %s\n", source_filename); + Value** patch_data, + Value* bonus_data) { + printf("patch %s: ", source_filename); if (target_filename[0] == '-' && target_filename[1] == '\0') { @@ -644,8 +747,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; } @@ -699,7 +803,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 +817,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; @@ -766,8 +871,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) { @@ -802,7 +909,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); } } @@ -867,7 +974,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; @@ -898,6 +1005,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) { diff --git a/applypatch/applypatch.h b/applypatch/applypatch.h index fb58843ba..f1f13a100 100644 --- a/applypatch/applypatch.h +++ b/applypatch/applypatch.h @@ -55,7 +55,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); int applypatch_check(const char* filename, int num_patches, char** const patch_sha1_str); @@ -64,7 +65,7 @@ int LoadFileContents(const char* filename, FileContents* file, int retouch_flag); int SaveFileContents(const char* filename, const FileContents* file); void FreeFileContents(FileContents* file); -int FindMatchingPatch(uint8_t* sha1, const char** patch_sha1_str, +int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str, int num_patches); // bsdiff.c @@ -79,7 +80,8 @@ int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, // imgpatch.c int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, - SinkFn sink, void* token, SHA_CTX* ctx); + SinkFn sink, void* token, SHA_CTX* ctx, + const Value* bonus_data); // freecache.c int MakeFreeSpaceOnCache(size_t bytes_needed); diff --git a/applypatch/imgdiff.c b/applypatch/imgdiff.c index 6b9ebee5c..05c4f250f 100644 --- a/applypatch/imgdiff.c +++ b/applypatch/imgdiff.c @@ -111,6 +111,14 @@ * * After the header there are 'chunk count' bsdiff patches; the offset * of each from the beginning of the file is specified in the header. + * + * This tool can take an optional file of "bonus data". This is an + * extra file of data that is appended to chunk #1 after it is + * compressed (it must be a CHUNK_DEFLATE chunk). The same file must + * be available (and passed to applypatch with -b) when applying the + * patch. This is used to reduce the size of recovery-from-boot + * patches by combining the boot image with recovery ramdisk + * information that is stored on the system partition. */ #include <errno.h> @@ -772,21 +780,45 @@ void DumpChunks(ImageChunk* chunks, int num_chunks) { } int main(int argc, char** argv) { - if (argc != 4 && argc != 5) { - usage: - printf("usage: %s [-z] <src-img> <tgt-img> <patch-file>\n", - argv[0]); - return 2; - } - int zip_mode = 0; - if (strcmp(argv[1], "-z") == 0) { + if (argc >= 2 && strcmp(argv[1], "-z") == 0) { zip_mode = 1; --argc; ++argv; } + size_t bonus_size = 0; + unsigned char* bonus_data = NULL; + if (argc >= 3 && strcmp(argv[1], "-b") == 0) { + struct stat st; + if (stat(argv[2], &st) != 0) { + printf("failed to stat bonus file %s: %s\n", argv[2], strerror(errno)); + return 1; + } + bonus_size = st.st_size; + bonus_data = malloc(bonus_size); + FILE* f = fopen(argv[2], "rb"); + if (f == NULL) { + printf("failed to open bonus file %s: %s\n", argv[2], strerror(errno)); + return 1; + } + if (fread(bonus_data, 1, bonus_size, f) != bonus_size) { + printf("failed to read bonus file %s: %s\n", argv[2], strerror(errno)); + return 1; + } + fclose(f); + + argc -= 2; + argv += 2; + } + + if (argc != 4) { + usage: + printf("usage: %s [-z] [-b <bonus-file>] <src-img> <tgt-img> <patch-file>\n", + argv[0]); + return 2; + } int num_src_chunks; ImageChunk* src_chunks; @@ -909,6 +941,8 @@ int main(int argc, char** argv) { // Compute bsdiff patches for each chunk's data (the uncompressed // data, in the case of deflate chunks). + DumpChunks(src_chunks, num_src_chunks); + printf("Construct patches for %d chunks...\n", num_tgt_chunks); unsigned char** patch_data = malloc(num_tgt_chunks * sizeof(unsigned char*)); size_t* patch_size = malloc(num_tgt_chunks * sizeof(size_t)); @@ -923,6 +957,13 @@ int main(int argc, char** argv) { patch_data[i] = MakePatch(src_chunks, tgt_chunks+i, patch_size+i); } } else { + if (i == 1 && bonus_data) { + printf(" using %d bytes of bonus data for chunk %d\n", bonus_size, i); + src_chunks[i].data = realloc(src_chunks[i].data, src_chunks[i].len + bonus_size); + memcpy(src_chunks[i].data+src_chunks[i].len, bonus_data, bonus_size); + src_chunks[i].len += bonus_size; + } + patch_data[i] = MakePatch(src_chunks+i, tgt_chunks+i, patch_size+i); } printf("patch %3d is %d bytes (of %d)\n", diff --git a/applypatch/imgpatch.c b/applypatch/imgpatch.c index e3ee80ac0..3a1df3872 100644 --- a/applypatch/imgpatch.c +++ b/applypatch/imgpatch.c @@ -37,7 +37,8 @@ */ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, - SinkFn sink, void* token, SHA_CTX* ctx) { + SinkFn sink, void* token, SHA_CTX* ctx, + const Value* bonus_data) { ssize_t pos = 12; char* header = patch->data; if (patch->size < 12) { @@ -123,6 +124,12 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, // Decompress the source data; the chunk header tells us exactly // how big we expect it to be when decompressed. + // Note: expanded_len will include the bonus data size if + // the patch was constructed with bonus data. The + // deflation will come up 'bonus_size' bytes short; these + // must be appended from the bonus_data value. + size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->size : 0; + unsigned char* expanded_source = malloc(expanded_len); if (expanded_source == NULL) { printf("failed to allocate %d bytes for expanded_source\n", @@ -153,13 +160,19 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, printf("source inflation returned %d\n", ret); return -1; } - // We should have filled the output buffer exactly. - if (strm.avail_out != 0) { - printf("source inflation short by %d bytes\n", strm.avail_out); + // We should have filled the output buffer exactly, except + // for the bonus_size. + if (strm.avail_out != bonus_size) { + printf("source inflation short by %d bytes\n", strm.avail_out-bonus_size); return -1; } inflateEnd(&strm); + if (bonus_size) { + memcpy(expanded_source + (expanded_len - bonus_size), + bonus_data->data, bonus_size); + } + // Next, apply the bsdiff patch (in memory) to the uncompressed // data. unsigned char* uncompressed_target_data; diff --git a/applypatch/main.c b/applypatch/main.c index 7025a2e2e..f61db5d9e 100644 --- a/applypatch/main.c +++ b/applypatch/main.c @@ -100,6 +100,21 @@ static int ParsePatchArgs(int argc, char** argv, } int PatchMode(int argc, char** argv) { + Value* bonus = NULL; + if (argc >= 3 && strcmp(argv[1], "-b") == 0) { + FileContents fc; + if (LoadFileContents(argv[2], &fc, RETOUCH_DONT_MASK) != 0) { + printf("failed to load bonus file %s\n", argv[2]); + return 1; + } + bonus = malloc(sizeof(Value)); + bonus->type = VAL_BLOB; + bonus->size = fc.size; + bonus->data = (char*)fc.data; + argc -= 2; + argv += 2; + } + if (argc < 6) { return 2; } @@ -120,7 +135,7 @@ int PatchMode(int argc, char** argv) { } int result = applypatch(argv[1], argv[2], argv[3], target_size, - num_patches, sha1s, patches); + num_patches, sha1s, patches, bonus); int i; for (i = 0; i < num_patches; ++i) { @@ -130,6 +145,10 @@ int PatchMode(int argc, char** argv) { free(p); } } + if (bonus) { + free(bonus->data); + free(bonus); + } free(sha1s); free(patches); @@ -163,7 +182,7 @@ int main(int argc, char** argv) { if (argc < 2) { usage: printf( - "usage: %s <src-file> <tgt-file> <tgt-sha1> <tgt-size> " + "usage: %s [-b <bonus-file>] <src-file> <tgt-file> <tgt-sha1> <tgt-size> " "[<src-sha1>:<patch> ...]\n" " or %s -c <file> [<sha1> ...]\n" " or %s -s <bytes>\n" diff --git a/bootloader.cpp b/bootloader.cpp index ff0dc6826..fbb31e060 100644 --- a/bootloader.cpp +++ b/bootloader.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +/* +#include <fs_mgr.h> +*/ #include "bootloader.h" #include "common.h" extern "C" { @@ -27,38 +30,42 @@ extern "C" { #include <sys/stat.h> #include <unistd.h> +static char device_type = 'e'; // e for emmc or m for mtd, default is emmc +static char device_name[256]; + +/* static int get_bootloader_message_mtd(struct bootloader_message *out, const Volume* v); static int set_bootloader_message_mtd(const struct bootloader_message *in, const Volume* v); static int get_bootloader_message_block(struct bootloader_message *out, const Volume* v); static int set_bootloader_message_block(const struct bootloader_message *in, const Volume* v); - +*/ int get_bootloader_message(struct bootloader_message *out) { - Volume* v = NULL;//volume_for_path("/misc"); - if (v == NULL) { + //volume_for_path("/misc"); + if (device_name[0] == 0) { LOGE("Cannot load volume /misc!\n"); return -1; } - if (strcmp(v->fs_type, "mtd") == 0) { - return get_bootloader_message_mtd(out, v); - } else if (strcmp(v->fs_type, "emmc") == 0) { - return get_bootloader_message_block(out, v); + if (device_type == 'm') { + return get_bootloader_message_mtd_name(out); + } else if (device_type == 'e') { + return get_bootloader_message_block_name(out); } - LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type); + LOGE("unknown misc partition fs_type \"%c\"\n", device_type); return -1; } int set_bootloader_message(const struct bootloader_message *in) { - Volume* v = NULL;//volume_for_path("/misc"); - if (v == NULL) { + //volume_for_path("/misc"); + if (device_name[0] == 0) { LOGE("Cannot load volume /misc!\n"); return -1; } - if (strcmp(v->fs_type, "mtd") == 0) { - return set_bootloader_message_mtd(in, v); - } else if (strcmp(v->fs_type, "emmc") == 0) { - return set_bootloader_message_block(in, v); + if (device_type == 'm') { + return set_bootloader_message_mtd_name(in, device_name); + } else if (device_type == 'e') { + return set_bootloader_message_block_name(in, device_name); } - LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type); + LOGE("unknown misc partition type \"%c\"\n", device_type); return -1; } @@ -68,27 +75,27 @@ int set_bootloader_message(const struct bootloader_message *in) { static const int MISC_PAGES = 3; // number of pages to save static const int MISC_COMMAND_PAGE = 1; // bootloader command is this page - +/* static int get_bootloader_message_mtd(struct bootloader_message *out, const Volume* v) { size_t write_size; mtd_scan_partitions(); - const MtdPartition *part = mtd_find_partition_by_name(v->device); + const MtdPartition *part = mtd_find_partition_by_name(v->blk_device); if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) { - LOGE("Can't find %s\n", v->device); + LOGE("Can't find %s\n", v->blk_device); return -1; } MtdReadContext *read = mtd_read_partition(part); if (read == NULL) { - LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno)); + LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno)); return -1; } const ssize_t size = write_size * MISC_PAGES; char data[size]; ssize_t r = mtd_read_data(read, data, size); - if (r != size) LOGE("Can't read %s\n(%s)\n", v->device, strerror(errno)); + if (r != size) LOGE("Can't read %s\n(%s)\n", v->blk_device, strerror(errno)); mtd_read_close(read); if (r != size) return -1; @@ -99,22 +106,22 @@ static int set_bootloader_message_mtd(const struct bootloader_message *in, const Volume* v) { size_t write_size; mtd_scan_partitions(); - const MtdPartition *part = mtd_find_partition_by_name(v->device); + const MtdPartition *part = mtd_find_partition_by_name(v->blk_device); if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) { - LOGE("Can't find %s\n", v->device); + LOGE("Can't find %s\n", v->blk_device); return -1; } MtdReadContext *read = mtd_read_partition(part); if (read == NULL) { - LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno)); + LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno)); return -1; } ssize_t size = write_size * MISC_PAGES; char data[size]; ssize_t r = mtd_read_data(read, data, size); - if (r != size) LOGE("Can't read %s\n(%s)\n", v->device, strerror(errno)); + if (r != size) LOGE("Can't read %s\n(%s)\n", v->blk_device, strerror(errno)); mtd_read_close(read); if (r != size) return -1; @@ -122,22 +129,61 @@ static int set_bootloader_message_mtd(const struct bootloader_message *in, MtdWriteContext *write = mtd_write_partition(part); if (write == NULL) { - LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno)); + LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno)); return -1; } if (mtd_write_data(write, data, size) != size) { - LOGE("Can't write %s\n(%s)\n", v->device, strerror(errno)); + LOGE("Can't write %s\n(%s)\n", v->blk_device, strerror(errno)); mtd_write_close(write); return -1; } if (mtd_write_close(write)) { - LOGE("Can't finish %s\n(%s)\n", v->device, strerror(errno)); + LOGE("Can't finish %s\n(%s)\n", v->blk_device, strerror(errno)); return -1; } LOGI("Set boot command \"%s\"\n", in->command[0] != 255 ? in->command : ""); return 0; } +*/ + +void set_device_type(char new_type) { + device_type = new_type; +} + +void set_device_name(const char* new_name) { + if (strlen(new_name) >= sizeof(device_name)) { + LOGE("New device name of '%s' is too large for bootloader.cpp\n", new_name); + } else { + strcpy(device_name, new_name); + } +} + +int get_bootloader_message_mtd_name(struct bootloader_message *out) { + size_t write_size; + mtd_scan_partitions(); + const MtdPartition *part = mtd_find_partition_by_name(device_name); + if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) { + LOGE("Can't find %s\n", device_name); + return -1; + } + + MtdReadContext *read = mtd_read_partition(part); + if (read == NULL) { + LOGE("Can't open %s\n(%s)\n", device_name, strerror(errno)); + return -1; + } + + const ssize_t size = write_size * MISC_PAGES; + char data[size]; + ssize_t r = mtd_read_data(read, data, size); + if (r != size) LOGE("Can't read %s\n(%s)\n", device_name, strerror(errno)); + mtd_read_close(read); + if (r != size) return -1; + + memcpy(out, &data[write_size * MISC_COMMAND_PAGE], sizeof(*out)); + return 0; +} int set_bootloader_message_mtd_name(const struct bootloader_message *in, const char* mtd_name) { @@ -203,23 +249,23 @@ static void wait_for_device(const char* fn) { printf("failed to stat %s\n", fn); } } - +/* static int get_bootloader_message_block(struct bootloader_message *out, const Volume* v) { - wait_for_device(v->device); - FILE* f = fopen(v->device, "rb"); + wait_for_device(v->blk_device); + FILE* f = fopen(v->blk_device, "rb"); if (f == NULL) { - LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno)); + LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno)); return -1; } struct bootloader_message temp; int count = fread(&temp, sizeof(temp), 1, f); if (count != 1) { - LOGE("Failed reading %s\n(%s)\n", v->device, strerror(errno)); + LOGE("Failed reading %s\n(%s)\n", v->blk_device, strerror(errno)); return -1; } if (fclose(f) != 0) { - LOGE("Failed closing %s\n(%s)\n", v->device, strerror(errno)); + LOGE("Failed closing %s\n(%s)\n", v->blk_device, strerror(errno)); return -1; } memcpy(out, &temp, sizeof(temp)); @@ -228,21 +274,43 @@ static int get_bootloader_message_block(struct bootloader_message *out, static int set_bootloader_message_block(const struct bootloader_message *in, const Volume* v) { - wait_for_device(v->device); - FILE* f = fopen(v->device, "wb"); + wait_for_device(v->blk_device); + FILE* f = fopen(v->blk_device, "wb"); if (f == NULL) { - LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno)); + LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno)); return -1; } int count = fwrite(in, sizeof(*in), 1, f); if (count != 1) { - LOGE("Failed writing %s\n(%s)\n", v->device, strerror(errno)); + LOGE("Failed writing %s\n(%s)\n", v->blk_device, strerror(errno)); + return -1; + } + if (fclose(f) != 0) { + LOGE("Failed closing %s\n(%s)\n", v->blk_device, strerror(errno)); + return -1; + } + return 0; +} +*/ + +int get_bootloader_message_block_name(struct bootloader_message *out) { + wait_for_device(device_name); + FILE* f = fopen(device_name, "rb"); + if (f == NULL) { + LOGE("Can't open %s\n(%s)\n", device_name, strerror(errno)); + return -1; + } + struct bootloader_message temp; + int count = fread(&temp, sizeof(temp), 1, f); + if (count != 1) { + LOGE("Failed reading %s\n(%s)\n", device_name, strerror(errno)); return -1; } if (fclose(f) != 0) { - LOGE("Failed closing %s\n(%s)\n", v->device, strerror(errno)); + LOGE("Failed closing %s\n(%s)\n", device_name, strerror(errno)); return -1; } + memcpy(out, &temp, sizeof(temp)); return 0; } diff --git a/bootloader.h b/bootloader.h index ead1d0b52..0a682b28a 100644 --- a/bootloader.h +++ b/bootloader.h @@ -48,10 +48,17 @@ struct bootloader_message { /* Read and write the bootloader command from the "misc" partition. * These return zero on success. */ +/* int get_bootloader_message(struct bootloader_message *out); int set_bootloader_message(const struct bootloader_message *in); +*/ + +void set_device_type(char new_type); +void set_device_name(const char* new_name); +int get_bootloader_message_mtd_name(struct bootloader_message *out); int set_bootloader_message_mtd_name(const struct bootloader_message *in, const char* mtd_name); +int get_bootloader_message_block_name(struct bootloader_message *out); int set_bootloader_message_block_name(const struct bootloader_message *in, const char* block_name); void get_args(int *argc, char ***argv); @@ -18,6 +18,7 @@ #define RECOVERY_COMMON_H #include <stdio.h> +#include <stdarg.h> #ifdef __cplusplus extern "C" { @@ -44,28 +45,13 @@ static long tmplog_offset = 0; #define STRINGIFY(x) #x #define EXPAND(x) STRINGIFY(x) -typedef struct { - const char* mount_point; // eg. "/cache". must live in the root directory. - - const char* fs_type; // "yaffs2" or "ext4" or "vfat" - - const char* device; // MTD partition name if fs_type == "yaffs" - // block device if fs_type == "ext4" or "vfat" - - const char* device2; // alternative device to try if fs_type - // == "ext4" or "vfat" and mounting - // 'device' fails - - long long length; // (ext4 partition only) when - // formatting, size to use for the - // partition. 0 or negative number - // means to format all but the last - // (that much). -} Volume; +typedef struct fstab_rec Volume; // fopen a file, mounting volumes and making parent dirs as necessary. FILE* fopen_path(const char *path, const char *mode); +//void ui_print(const char* format, ...); + #ifdef __cplusplus } #endif diff --git a/fonts/12x22.png b/fonts/12x22.png Binary files differnew file mode 100644 index 000000000..ae826bebe --- /dev/null +++ b/fonts/12x22.png diff --git a/fonts/18x32.png b/fonts/18x32.png Binary files differnew file mode 100644 index 000000000..d95408a93 --- /dev/null +++ b/fonts/18x32.png diff --git a/fonts/OFL.txt b/fonts/OFL.txt new file mode 100644 index 000000000..b14edde76 --- /dev/null +++ b/fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright (c) 2011, Raph Levien (firstname.lastname@gmail.com), Copyright (c) 2012, Cyreal (cyreal.org) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/fonts/README b/fonts/README new file mode 100644 index 000000000..d0748d2f7 --- /dev/null +++ b/fonts/README @@ -0,0 +1,6 @@ +The images in this directory were generated using the font +Inconsolata, which is released under the OFL license and was obtained +from: + + https://code.google.com/p/googlefontdirectory/source/browse/ofl/inconsolata/ + diff --git a/install.cpp b/install.cpp index 92388e8f3..0f3298f1d 100644 --- a/install.cpp +++ b/install.cpp @@ -174,86 +174,10 @@ try_update_binary(const char *path, ZipArchive *zip, int* wipe_cache) { return INSTALL_SUCCESS; } -// Reads a file containing one or more public keys as produced by -// DumpPublicKey: this is an RSAPublicKey struct as it would appear -// as a C source literal, eg: -// -// "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}" -// -// (Note that the braces and commas in this example are actual -// characters the parser expects to find in the file; the ellipses -// indicate more numbers omitted from this example.) -// -// The file may contain multiple keys in this format, separated by -// commas. The last key must not be followed by a comma. -// -// Returns NULL if the file failed to parse, or if it contain zero keys. -RSAPublicKey* -load_keys(const char* filename, int* numKeys) { - RSAPublicKey* out = NULL; - *numKeys = 0; - - FILE* f = fopen(filename, "r"); - if (f == NULL) { - LOGE("opening %s: %s\n", filename, strerror(errno)); - goto exit; - } - - { - int i; - bool done = false; - while (!done) { - ++*numKeys; - out = (RSAPublicKey*)realloc(out, *numKeys * sizeof(RSAPublicKey)); - RSAPublicKey* key = out + (*numKeys - 1); - if (fscanf(f, " { %i , 0x%x , { %u", - &(key->len), &(key->n0inv), &(key->n[0])) != 3) { - goto exit; - } - if (key->len != RSANUMWORDS) { - LOGE("key length (%d) does not match expected size\n", key->len); - goto exit; - } - for (i = 1; i < key->len; ++i) { - if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit; - } - if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit; - for (i = 1; i < key->len; ++i) { - if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit; - } - fscanf(f, " } } "); - - // if the line ends in a comma, this file has more keys. - switch (fgetc(f)) { - case ',': - // more keys to come. - break; - - case EOF: - done = true; - break; - - default: - LOGE("unexpected character between keys\n"); - goto exit; - } - } - } - - fclose(f); - return out; - -exit: - if (f) fclose(f); - free(out); - *numKeys = 0; - return NULL; -} - static int really_install_package(const char *path, int* wipe_cache) { - ui->SetBackground(RecoveryUI::INSTALLING); + ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); ui->Print("Finding update package...\n"); ui->SetProgressType(RecoveryUI::INDETERMINATE); LOGI("Update location: %s\n", path); @@ -24,7 +24,7 @@ extern "C" { #endif -enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT }; +enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE }; // Install the package specified by root_path. If INSTALL_SUCCESS is // returned and *wipe_cache is true on exit, caller should wipe the // cache partition. diff --git a/minadbd/adb.c b/minadbd/adb.c index 54adba050..4cd05ed3c 100644 --- a/minadbd/adb.c +++ b/minadbd/adb.c @@ -29,8 +29,6 @@ #include "adb.h" #include <private/android_filesystem_config.h> -#include <linux/capability.h> -#include <linux/prctl.h> #if ADB_TRACE ADB_MUTEX_DEFINE( D_lock ); diff --git a/minui/Android.mk b/minui/Android.mk index f9afd6ba9..232ebb2bf 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -9,11 +9,21 @@ LOCAL_C_INCLUDES +=\ LOCAL_STATIC_LIBRARY := libpng LOCAL_MODULE := libminui -ifeq ($(TARGET_RECOVERY_PIXEL_FORMAT),"RGBX_8888") +# This used to compare against values in double-quotes (which are just +# ordinary characters in this context). Strip double-quotes from the +# value so that either will work. + +ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),RGBX_8888) LOCAL_CFLAGS += -DRECOVERY_RGBX endif -ifeq ($(TARGET_RECOVERY_PIXEL_FORMAT),"BGRA_8888") +ifeq ($(subst ",,$(TARGET_RECOVERY_PIXEL_FORMAT)),BGRA_8888) LOCAL_CFLAGS += -DRECOVERY_BGRA endif +ifneq ($(TARGET_RECOVERY_OVERSCAN_PERCENT),) + LOCAL_CFLAGS += -DOVERSCAN_PERCENT=$(TARGET_RECOVERY_OVERSCAN_PERCENT) +else + LOCAL_CFLAGS += -DOVERSCAN_PERCENT=0 +endif + include $(BUILD_STATIC_LIBRARY) diff --git a/minui/graphics.c b/minui/graphics.c index 296e2e078..4968eac7a 100644 --- a/minui/graphics.c +++ b/minui/graphics.c @@ -44,20 +44,24 @@ #define PIXEL_SIZE 2 #endif +#define NUM_BUFFERS 2 + typedef struct { - GGLSurface texture; + GGLSurface* texture; unsigned cwidth; unsigned cheight; - unsigned ascent; } GRFont; static GRFont *gr_font = 0; static GGLContext *gr_context = 0; static GGLSurface gr_font_texture; -static GGLSurface gr_framebuffer[2]; +static GGLSurface gr_framebuffer[NUM_BUFFERS]; static GGLSurface gr_mem_surface; static unsigned gr_active_fb = 0; static unsigned double_buffering = 0; +static int overscan_percent = OVERSCAN_PERCENT; +static int overscan_offset_x = 0; +static int overscan_offset_y = 0; static int gr_fb_fd = -1; static int gr_vt_fd = -1; @@ -130,6 +134,9 @@ static int get_framebuffer(GGLSurface *fb) return -1; } + overscan_offset_x = vi.xres * overscan_percent / 100; + overscan_offset_y = vi.yres * overscan_percent / 100; + fb->version = sizeof(*fb); fb->width = vi.xres; fb->height = vi.yres; @@ -169,7 +176,7 @@ static void get_memory_surface(GGLSurface* ms) { static void set_active_framebuffer(unsigned n) { if (n > 1 || !double_buffering) return; - vi.yres_virtual = vi.yres * PIXEL_SIZE; + vi.yres_virtual = vi.yres * NUM_BUFFERS; vi.yoffset = n * vi.yres; vi.bits_per_pixel = PIXEL_SIZE * 8; if (ioctl(gr_fb_fd, FBIOPUT_VSCREENINFO, &vi) < 0) { @@ -216,15 +223,20 @@ void gr_font_size(int *x, int *y) *y = gr_font->cheight; } -int gr_text(int x, int y, const char *s) +int gr_text(int x, int y, const char *s, int bold) { GGLContext *gl = gr_context; GRFont *font = gr_font; unsigned off; - y -= font->ascent; + if (!font->texture) return x; + + bold = bold && (font->texture->height != font->cheight); - gl->bindTexture(gl, &font->texture); + x += overscan_offset_x; + y += overscan_offset_y; + + gl->bindTexture(gl, font->texture); gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); @@ -233,7 +245,8 @@ int gr_text(int x, int y, const char *s) while((off = *s++)) { off -= 32; if (off < 96) { - gl->texCoord2i(gl, (off * font->cwidth) - x, 0 - y); + gl->texCoord2i(gl, (off * font->cwidth) - x, + (bold ? font->cheight : 0) - y); gl->recti(gl, x, y, x + font->cwidth, y + font->cheight); } x += font->cwidth; @@ -242,19 +255,50 @@ int gr_text(int x, int y, const char *s) return x; } -void gr_fill(int x, int y, int w, int h) +void gr_texticon(int x, int y, gr_surface icon) { + if (gr_context == NULL || icon == NULL) { + return; + } + GGLContext* gl = gr_context; + + x += overscan_offset_x; + y += overscan_offset_y; + + gl->bindTexture(gl, (GGLSurface*) icon); + gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); + gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + gl->texGeni(gl, GGL_T, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); + gl->enable(gl, GGL_TEXTURE_2D); + + int w = gr_get_width(icon); + int h = gr_get_height(icon); + + gl->texCoord2i(gl, -x, -y); + gl->recti(gl, x, y, x+gr_get_width(icon), y+gr_get_height(icon)); +} + +void gr_fill(int x1, int y1, int x2, int y2) { + x1 += overscan_offset_x; + y1 += overscan_offset_y; + + x2 += overscan_offset_x; + y2 += overscan_offset_y; + GGLContext *gl = gr_context; gl->disable(gl, GGL_TEXTURE_2D); - gl->recti(gl, x, y, w, h); + gl->recti(gl, x1, y1, x2, y2); } void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy) { - if (gr_context == NULL) { + if (gr_context == NULL || source == NULL) { return; } GGLContext *gl = gr_context; + dx += overscan_offset_x; + dy += overscan_offset_y; + gl->bindTexture(gl, (GGLSurface*) source); gl->texEnvi(gl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); gl->texGeni(gl, GGL_S, GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); @@ -280,31 +324,40 @@ unsigned int gr_get_height(gr_surface surface) { static void gr_init_font(void) { - GGLSurface *ftex; - unsigned char *bits, *rle; - unsigned char *in, data; - gr_font = calloc(sizeof(*gr_font), 1); - ftex = &gr_font->texture; - bits = malloc(font.width * font.height); - - ftex->version = sizeof(*ftex); - ftex->width = font.width; - ftex->height = font.height; - ftex->stride = font.width; - ftex->data = (void*) bits; - ftex->format = GGL_PIXEL_FORMAT_A_8; + int res = res_create_surface("font", (void**)&(gr_font->texture)); + if (res == 0) { + // The font image should be a 96x2 array of character images. The + // columns are the printable ASCII characters 0x20 - 0x7f. The + // top row is regular text; the bottom row is bold. + gr_font->cwidth = gr_font->texture->width / 96; + gr_font->cheight = gr_font->texture->height / 2; + } else { + printf("failed to read font: res=%d\n", res); + + // fall back to the compiled-in font. + gr_font->texture = malloc(sizeof(*gr_font->texture)); + gr_font->texture->width = font.width; + gr_font->texture->height = font.height; + gr_font->texture->stride = font.width; + + unsigned char* bits = malloc(font.width * font.height); + gr_font->texture->data = (void*) bits; + + unsigned char data; + unsigned char* in = font.rundata; + while((data = *in++)) { + memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f); + bits += (data & 0x7f); + } - in = font.rundata; - while((data = *in++)) { - memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f); - bits += (data & 0x7f); + gr_font->cwidth = font.cwidth; + gr_font->cheight = font.cheight; } - gr_font->cwidth = font.cwidth; - gr_font->cheight = font.cheight; - gr_font->ascent = font.cheight - 2; + // interpret the grayscale as alpha + gr_font->texture->format = GGL_PIXEL_FORMAT_A_8; } int gr_init(void) @@ -364,12 +417,12 @@ void gr_exit(void) int gr_fb_width(void) { - return gr_framebuffer[0].width; + return gr_framebuffer[0].width - 2*overscan_offset_x; } int gr_fb_height(void) { - return gr_framebuffer[0].height; + return gr_framebuffer[0].height - 2*overscan_offset_y; } gr_pixel *gr_fb_data(void) diff --git a/minui/minui.h b/minui/minui.h index 74da4e9c0..1b8dd059b 100644 --- a/minui/minui.h +++ b/minui/minui.h @@ -36,8 +36,9 @@ void gr_flip(void); void gr_fb_blank(bool blank); void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a); -void gr_fill(int x, int y, int w, int h); -int gr_text(int x, int y, const char *s); +void gr_fill(int x1, int y1, int x2, int y2); +int gr_text(int x, int y, const char *s, int bold); + void gr_texticon(int x, int y, gr_surface icon); int gr_measure(const char *s); void gr_font_size(int *x, int *y); @@ -71,6 +72,7 @@ void ev_dispatch(void); // Returns 0 if no error, else negative. int res_create_surface(const char* name, gr_surface* pSurface); +int res_create_localized_surface(const char* name, gr_surface* pSurface); void res_free_surface(gr_surface surface); #ifdef __cplusplus diff --git a/minui/resources.c b/minui/resources.c index 5e2062192..c0a9ccacb 100644 --- a/minui/resources.c +++ b/minui/resources.c @@ -33,6 +33,8 @@ #include "minui.h" +extern char* locale; + // libpng gives "undefined reference to 'pow'" errors, and I have no // idea how to convince the build system to link with -lm. We don't // need this functionality (it's used for gamma adjustment) so provide @@ -91,22 +93,25 @@ int res_create_surface(const char* name, gr_surface* pSurface) { png_set_sig_bytes(png_ptr, sizeof(header)); png_read_info(png_ptr, info_ptr); - size_t width = info_ptr->width; - size_t height = info_ptr->height; - size_t stride = 4 * width; - size_t pixelSize = stride * height; + int color_type, bit_depth; + size_t width, height; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, + &color_type, NULL, NULL, NULL); + + int channels = png_get_channels(png_ptr, info_ptr); - int color_type = info_ptr->color_type; - int bit_depth = info_ptr->bit_depth; - int channels = info_ptr->channels; if (!(bit_depth == 8 && ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) || (channels == 4 && color_type == PNG_COLOR_TYPE_RGBA) || - (channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE)))) { + (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE || + color_type == PNG_COLOR_TYPE_GRAY))))) { return -7; goto exit; } + size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width; + size_t pixelSize = stride * height; + surface = malloc(sizeof(GGLSurface) + pixelSize); if (surface == NULL) { result = -8; @@ -118,8 +123,8 @@ int res_create_surface(const char* name, gr_surface* pSurface) { surface->height = height; surface->stride = width; /* Yes, pixels, not bytes */ surface->data = pData; - surface->format = (channels == 3) ? - GGL_PIXEL_FORMAT_RGBX_8888 : GGL_PIXEL_FORMAT_RGBA_8888; + surface->format = (channels == 3) ? GGL_PIXEL_FORMAT_RGBX_8888 : + ((color_type == PNG_COLOR_TYPE_PALETTE ? GGL_PIXEL_FORMAT_RGBA_8888 : GGL_PIXEL_FORMAT_L_8)); int alpha = 0; if (color_type == PNG_COLOR_TYPE_PALETTE) { @@ -129,6 +134,9 @@ int res_create_surface(const char* name, gr_surface* pSurface) { png_set_tRNS_to_alpha(png_ptr); alpha = 1; } + if (color_type == PNG_COLOR_TYPE_GRAY) { + alpha = 1; + } unsigned int y; if (channels == 3 || (channels == 1 && !alpha)) { @@ -173,6 +181,141 @@ exit: return result; } +static int matches_locale(const char* loc) { + if (locale == NULL) return 0; + + if (strcmp(loc, locale) == 0) return 1; + + // if loc does *not* have an underscore, and it matches the start + // of locale, and the next character in locale *is* an underscore, + // that's a match. For instance, loc == "en" matches locale == + // "en_US". + + int i; + for (i = 0; loc[i] != 0 && loc[i] != '_'; ++i); + if (loc[i] == '_') return 0; + + return (strncmp(locale, loc, i) == 0 && locale[i] == '_'); +} + +int res_create_localized_surface(const char* name, gr_surface* pSurface) { + char resPath[256]; + GGLSurface* surface = NULL; + int result = 0; + unsigned char header[8]; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + + *pSurface = NULL; + + snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name); + resPath[sizeof(resPath)-1] = '\0'; + FILE* fp = fopen(resPath, "rb"); + if (fp == NULL) { + result = -1; + goto exit; + } + + size_t bytesRead = fread(header, 1, sizeof(header), fp); + if (bytesRead != sizeof(header)) { + result = -2; + goto exit; + } + + if (png_sig_cmp(header, 0, sizeof(header))) { + result = -3; + goto exit; + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + result = -4; + goto exit; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + result = -5; + goto exit; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + result = -6; + goto exit; + } + + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, sizeof(header)); + png_read_info(png_ptr, info_ptr); + + int color_type, bit_depth; + size_t width, height; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, + &color_type, NULL, NULL, NULL); + int channels = png_get_channels(png_ptr, info_ptr); + + if (!(bit_depth == 8 && + (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) { + return -7; + goto exit; + } + + unsigned char* row = malloc(width); + int y; + for (y = 0; y < height; ++y) { + png_read_row(png_ptr, row, NULL); + int w = (row[1] << 8) | row[0]; + int h = (row[3] << 8) | row[2]; + int len = row[4]; + char* loc = row+5; + + if (y+1+h >= height || matches_locale(loc)) { + printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y); + + surface = malloc(sizeof(GGLSurface)); + if (surface == NULL) { + result = -8; + goto exit; + } + unsigned char* pData = malloc(w*h); + + surface->version = sizeof(GGLSurface); + surface->width = w; + surface->height = h; + surface->stride = w; /* Yes, pixels, not bytes */ + surface->data = pData; + surface->format = GGL_PIXEL_FORMAT_A_8; + + int i; + for (i = 0; i < h; ++i, ++y) { + png_read_row(png_ptr, row, NULL); + memcpy(pData + i*w, row, w); + } + + *pSurface = (gr_surface) surface; + break; + } else { + int i; + for (i = 0; i < h; ++i, ++y) { + png_read_row(png_ptr, row, NULL); + } + } + } + +exit: + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + if (fp != NULL) { + fclose(fp); + } + if (result < 0) { + if (surface) { + free(surface); + } + } + return result; +} + void res_free_surface(gr_surface surface) { GGLSurface* pSurface = (GGLSurface*) surface; if (pSurface) { diff --git a/minzip/Android.mk b/minzip/Android.mk index 0435c6afb..3dd97ff04 100644 --- a/minzip/Android.mk +++ b/minzip/Android.mk @@ -8,15 +8,11 @@ LOCAL_SRC_FILES := \ Inlines.c \ Zip.c -LOCAL_C_INCLUDES += \ +LOCAL_C_INCLUDES := \ external/zlib \ external/safe-iop/include -ifeq ($(HAVE_SELINUX),true) -LOCAL_C_INCLUDES += external/libselinux/include -LOCAL_STATIC_LIBRARIES += libselinux -LOCAL_CFLAGS += -DHAVE_SELINUX -endif +LOCAL_STATIC_LIBRARIES := libselinux LOCAL_MODULE := libminzip diff --git a/minzip/DirUtil.c b/minzip/DirUtil.c index 0d49b5780..8dd5da1da 100644 --- a/minzip/DirUtil.c +++ b/minzip/DirUtil.c @@ -145,24 +145,19 @@ dirCreateHierarchy(const char *path, int mode, } else if (ds == DMISSING) { int err; -#ifdef HAVE_SELINUX char *secontext = NULL; if (sehnd) { selabel_lookup(sehnd, &secontext, cpath, mode); setfscreatecon(secontext); } -#endif err = mkdir(cpath, mode); -#ifdef HAVE_SELINUX - if (secontext) { freecon(secontext); setfscreatecon(NULL); } -#endif if (err != 0) { free(cpath); diff --git a/minzip/DirUtil.h b/minzip/DirUtil.h index f8be64026..a5cfa761b 100644 --- a/minzip/DirUtil.h +++ b/minzip/DirUtil.h @@ -24,12 +24,8 @@ extern "C" { #endif -#ifdef HAVE_SELINUX #include <selinux/selinux.h> #include <selinux/label.h> -#else -struct selabel_handle; -#endif /* Like "mkdir -p", try to guarantee that all directories * specified in path are present, creating as many directories diff --git a/minzip/SysUtil.c b/minzip/SysUtil.c index 49a2522d6..31c76d6d4 100644 --- a/minzip/SysUtil.c +++ b/minzip/SysUtil.c @@ -95,16 +95,16 @@ int sysLoadFileInShmem(int fd, MemMapping* pMap) if (memPtr == NULL) return -1; - actual = read(fd, memPtr, length); + pMap->baseAddr = pMap->addr = memPtr; + pMap->baseLength = pMap->length = length; + + actual = TEMP_FAILURE_RETRY(read(fd, memPtr, length)); if (actual != length) { LOGE("only read %d of %d bytes\n", (int) actual, (int) length); sysReleaseShmem(pMap); return -1; } - pMap->baseAddr = pMap->addr = memPtr; - pMap->baseLength = pMap->length = length; - return 0; } diff --git a/minzip/Zip.c b/minzip/Zip.c index 54d5d55a3..439e5d9cd 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -985,6 +985,7 @@ bool mzExtractRecursive(const ZipArchive *pArchive, unsigned int i; bool seenMatch = false; int ok = true; + int extractCount = 0; for (i = 0; i < pArchive->numEntries; i++) { ZipEntry *pEntry = pArchive->pEntries + i; if (pEntry->fileNameLen < zipDirLen) { @@ -1115,23 +1116,19 @@ bool mzExtractRecursive(const ZipArchive *pArchive, * Open the target for writing. */ -#ifdef HAVE_SELINUX char *secontext = NULL; if (sehnd) { selabel_lookup(sehnd, &secontext, targetFile, UNZIP_FILEMODE); setfscreatecon(secontext); } -#endif int fd = creat(targetFile, UNZIP_FILEMODE); -#ifdef HAVE_SELINUX if (secontext) { freecon(secontext); setfscreatecon(NULL); } -#endif if (fd < 0) { LOGE("Can't create target file \"%s\": %s\n", @@ -1154,13 +1151,16 @@ bool mzExtractRecursive(const ZipArchive *pArchive, break; } - LOGD("Extracted file \"%s\"\n", targetFile); + LOGV("Extracted file \"%s\"\n", targetFile); + ++extractCount; } } if (callback != NULL) callback(targetFile, cookie); } + LOGD("Extracted %d file(s)\n", extractCount); + free(helper.buf); free(zpath); diff --git a/minzip/Zip.h b/minzip/Zip.h index 4bb9ef6a4..c94282827 100644 --- a/minzip/Zip.h +++ b/minzip/Zip.h @@ -18,12 +18,8 @@ extern "C" { #endif -#ifdef HAVE_SELINUX #include <selinux/selinux.h> #include <selinux/label.h> -#else -struct selabel_handle; -#endif /* * One entry in the Zip archive. Treat this as opaque -- use accessors below. diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk index 8e1bdca7d..c1b0e5baa 100644 --- a/mtdutils/Android.mk +++ b/mtdutils/Android.mk @@ -50,8 +50,8 @@ LOCAL_MODULE_PATH := $(PRODUCT_OUT)/utilities LOCAL_UNSTRIPPED_PATH := $(PRODUCT_OUT)/symbols/utilities LOCAL_MODULE_STEM := bml_over_mtd LOCAL_C_INCLUDES += bootable/recovery/mtdutils -LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc -LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_STATIC_LIBRARIES := libmtdutils +LOCAL_SHARED_LIBRARIES := libcutils liblog libc include $(BUILD_EXECUTABLE) endif diff --git a/recovery.cpp b/recovery.cpp index 4f07fd44c..9308eec04 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -15,11 +15,13 @@ */ #include <ctype.h> +#include <dirent.h> #include <errno.h> #include <fcntl.h> #include <getopt.h> #include <limits.h> #include <linux/input.h> +#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -27,7 +29,6 @@ #include <sys/types.h> #include <time.h> #include <unistd.h> -#include <dirent.h> #include "bootloader.h" #include "common.h" @@ -70,15 +71,17 @@ static const struct option OPTIONS[] = { { "wipe_cache", no_argument, NULL, 'c' }, { "show_text", no_argument, NULL, 't' }, { "just_exit", no_argument, NULL, 'x' }, - { "nandroid", no_argument, NULL, 'n' }, + { "locale", required_argument, NULL, 'l' }, { NULL, 0, NULL, 0 }, }; +#define LAST_LOG_FILE "/cache/recovery/last_log" + static const char *COMMAND_FILE = "/cache/recovery/command"; static const char *INTENT_FILE = "/cache/recovery/intent"; static const char *LOG_FILE = "/cache/recovery/log"; -static const char *LAST_LOG_FILE = "/cache/recovery/last_log"; static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install"; +static const char *LOCALE_FILE = "/cache/recovery/last_locale"; static const char *CACHE_ROOT = "/cache"; static const char *SDCARD_ROOT = "/sdcard"; static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; @@ -86,6 +89,7 @@ static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload"; RecoveryUI* ui = NULL; +char* locale = NULL; /* * The recovery tool communicates with the main system through /cache files. @@ -212,6 +216,7 @@ get_args(int *argc, char ***argv) { if (*argc <= 1) { FILE *fp = fopen_path(COMMAND_FILE, "r"); if (fp != NULL) { + char *token; char *argv0 = (*argv)[0]; *argv = (char **) malloc(sizeof(char *) * MAX_ARGS); (*argv)[0] = argv0; // use the same program name @@ -219,7 +224,12 @@ get_args(int *argc, char ***argv) { char buf[MAX_ARG_LENGTH]; for (*argc = 1; *argc < MAX_ARGS; ++*argc) { if (!fgets(buf, sizeof(buf), fp)) break; - (*argv)[*argc] = strdup(strtok(buf, "\r\n")); // Strip newline. + token = strtok(buf, "\r\n"); + if (token != NULL) { + (*argv)[*argc] = strdup(token); // Strip newline. + } else { + --*argc; + } } check_and_fclose(fp, COMMAND_FILE); @@ -273,6 +283,21 @@ copy_log_file(const char* source, const char* destination, int append) { } } +// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max +// Overwrites any existing last_log.$max. +static void +rotate_last_logs(int max) { + char oldfn[256]; + char newfn[256]; + + int i; + for (i = max-1; i >= 0; --i) { + snprintf(oldfn, sizeof(oldfn), (i==0) ? LAST_LOG_FILE : (LAST_LOG_FILE ".%d"), i); + snprintf(newfn, sizeof(newfn), LAST_LOG_FILE ".%d", i+1); + // ignore errors + rename(oldfn, newfn); + } +} // clear the recovery command and prepare to boot a (hopefully working) system, // copy our log file to cache as well (for the system to read), and @@ -291,6 +316,18 @@ finish_recovery(const char *send_intent) { } } + // Save the locale to cache, so if recovery is next started up + // without a --locale argument (eg, directly from the bootloader) + // it will use the last-known locale. + if (locale != NULL) { + LOGI("Saving locale \"%s\"\n", locale); + FILE* fp = fopen_path(LOCALE_FILE, "w"); + fwrite(locale, 1, strlen(locale), fp); + fflush(fp); + fsync(fileno(fp)); + check_and_fclose(fp, LOCALE_FILE); + } + // Copy logs to cache so the system can find out what happened. copy_log_file(TEMPORARY_LOG_FILE, LOG_FILE, true); copy_log_file(TEMPORARY_LOG_FILE, LAST_LOG_FILE, false); @@ -317,8 +354,7 @@ finish_recovery(const char *send_intent) { static int erase_volume(const char *volume) { - return !PartitionManager.Wipe_By_Path(volume); - ui->SetBackground(RecoveryUI::INSTALLING); + ui->SetBackground(RecoveryUI::ERASING); ui->SetProgressType(RecoveryUI::INDETERMINATE); ui->Print("Formatting %s...\n", volume); @@ -671,11 +707,22 @@ wipe_data(int confirm, Device* device) { } static void -prompt_and_wait(Device* device) { +prompt_and_wait(Device* device, int status) { const char* const* headers = prepend_title(device->GetMenuHeaders()); for (;;) { finish_recovery(NULL); + switch (status) { + case INSTALL_SUCCESS: + case INSTALL_NONE: + ui->SetBackground(RecoveryUI::NO_COMMAND); + break; + + case INSTALL_ERROR: + case INSTALL_CORRUPT: + ui->SetBackground(RecoveryUI::ERROR); + break; + } ui->SetProgressType(RecoveryUI::EMPTY); int chosen_item = get_menu_selection(headers, device->GetMenuItems(), 0, 0, device); @@ -685,7 +732,6 @@ prompt_and_wait(Device* device) { // statement below. chosen_item = device->InvokeMenuItem(chosen_item); - int status; int wipe_cache; switch (chosen_item) { case Device::REBOOT: @@ -775,6 +821,43 @@ print_property(const char *key, const char *name, void *cookie) { printf("%s=%s\n", key, name); } +static void +load_locale_from_cache() { + FILE* fp = fopen_path(LOCALE_FILE, "r"); + char buffer[80]; + if (fp != NULL) { + fgets(buffer, sizeof(buffer), fp); + int j = 0; + unsigned int i; + for (i = 0; i < sizeof(buffer) && buffer[i]; ++i) { + if (!isspace(buffer[i])) { + buffer[j++] = buffer[i]; + } + } + buffer[j] = 0; + locale = strdup(buffer); + check_and_fclose(fp, LOCALE_FILE); + } +} + +static RecoveryUI* gCurrentUI = NULL; + +void +ui_print(const char* format, ...) { + char buffer[256]; + + va_list ap; + va_start(ap, format); + vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + + if (gCurrentUI != NULL) { + gCurrentUI->Print("%s", buffer); + } else { + fputs(buffer, stdout); + } +} + int main(int argc, char **argv) { // Recovery needs to install world-readable files, so clear umask @@ -824,12 +907,17 @@ main(int argc, char **argv) { gui_loadResources(); PartitionManager.Mount_By_Path("/cache", true); + + load_volume_table(); + ensure_path_mounted(LAST_LOG_FILE); + rotate_last_logs(5); + get_args(&argc, &argv); int previous_runs = 0; const char *send_intent = NULL; const char *update_package = NULL; - int wipe_data = 0, wipe_cache = 0; + int wipe_data = 0, wipe_cache = 0, show_text = 0; bool just_exit = false; bool perform_backup = false; @@ -841,16 +929,29 @@ main(int argc, char **argv) { case 'u': update_package = optarg; break; case 'w': wipe_data = wipe_cache = 1; break; case 'c': wipe_cache = 1; break; - case 't': ui->ShowText(true); break; + case 't': show_text = 1; break; case 'x': just_exit = true; break; - case 'n': perform_backup = true; LOGI("nandroid\n"); break; + case 'l': locale = optarg; break; case '?': LOGE("Invalid command argument\n"); continue; } } -#ifdef HAVE_SELINUX + if (locale == NULL) { + load_locale_from_cache(); + } + printf("locale is [%s]\n", locale); + + Device* device = make_device(); + ui = device->GetUI(); + gCurrentUI = ui; + + ui->Init(); + ui->SetLocale(locale); + ui->SetBackground(RecoveryUI::NONE); + if (show_text) ui->ShowText(true); + struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "/file_contexts" } }; @@ -861,7 +962,6 @@ main(int argc, char **argv) { fprintf(stderr, "Warning: No file_contexts\n"); ui->Print("Warning: No file_contexts\n"); } -#endif //device->StartRecovery(); @@ -936,8 +1036,19 @@ main(int argc, char **argv) { LOGE("Cache wipe (requested by package) failed."); } } - if (status != INSTALL_SUCCESS) ui->Print("Installation aborted.\n"); - */ + + if (status != INSTALL_SUCCESS) { + ui->Print("Installation aborted.\n"); + + // If this is an eng or userdebug build, then automatically + // turn the text display on if the script fails so the error + // message is visible. + char buffer[PROPERTY_VALUE_MAX+1]; + property_get("ro.build.fingerprint", buffer, ""); + if (strstr(buffer, ":userdebug/") || strstr(buffer, ":eng/")) { + ui->ShowText(true); + } + } } else if (wipe_data) { if (!OpenRecoveryScript::Insert_ORS_Command("wipe data\n")) status = INSTALL_ERROR; @@ -952,7 +1063,8 @@ main(int argc, char **argv) { status = INSTALL_ERROR; if (status != INSTALL_SUCCESS) ui->Print("Cache wipe failed.\n"); } else if (!just_exit) { - status = INSTALL_ERROR; // No command specified + status = INSTALL_NONE; // No command specified + ui->SetBackground(RecoveryUI::NO_COMMAND); } } @@ -998,6 +1110,13 @@ main(int argc, char **argv) { PartitionManager.UnMount_By_Path("/system", false); } + if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) { + ui->SetBackground(RecoveryUI::ERROR); + } + if (status != INSTALL_SUCCESS || ui->IsTextVisible()) { + prompt_and_wait(device, status); + } + // Otherwise, get ready to boot the main system... finish_recovery(send_intent); ui->Print("Rebooting...\n"); diff --git a/res/images/erasing_text.png b/res/images/erasing_text.png Binary files differnew file mode 100644 index 000000000..441768a0c --- /dev/null +++ b/res/images/erasing_text.png diff --git a/res/images/error_text.png b/res/images/error_text.png Binary files differnew file mode 100644 index 000000000..4ac6391ff --- /dev/null +++ b/res/images/error_text.png diff --git a/res/images/installing_text.png b/res/images/installing_text.png Binary files differnew file mode 100644 index 000000000..e1ac819e6 --- /dev/null +++ b/res/images/installing_text.png diff --git a/res/images/no_command_text.png b/res/images/no_command_text.png Binary files differnew file mode 100644 index 000000000..a688f0935 --- /dev/null +++ b/res/images/no_command_text.png @@ -23,6 +23,7 @@ #include <ctype.h> extern "C" { +#include <fs_mgr.h> #include "mtdutils/mtdutils.h" #include "mtdutils/mounts.h" } @@ -31,115 +32,41 @@ extern "C" { #include "make_ext4fs.h" #include "partitions.hpp" -static int num_volumes = 0; -static Volume* device_volumes = NULL; +static struct fstab *fstab = NULL; extern struct selabel_handle *sehandle; -static int parse_options(char* options, Volume* volume) { - char* option; - while ((option = strtok(options, ","))) { - options = NULL; - - if (strncmp(option, "flags=", 6) == 0) continue; - if (strncmp(option, "length=", 7) == 0) { - volume->length = strtoll(option+7, NULL, 10); - } else { - LOGE("bad option \"%s\"\n", option); - return -1; - } - } - return 0; -} - -void load_volume_table() { - int alloc = 2; - device_volumes = (Volume*)malloc(alloc * sizeof(Volume)); - - // Insert an entry for /tmp, which is the ramdisk and is always mounted. - device_volumes[0].mount_point = "/tmp"; - device_volumes[0].fs_type = "ramdisk"; - device_volumes[0].device = NULL; - device_volumes[0].device2 = NULL; - device_volumes[0].length = 0; - num_volumes = 1; +void load_volume_table() +{ + int i; + int ret; - FILE* fstab = fopen("/etc/recovery.fstab", "r"); - if (fstab == NULL) { - LOGE("failed to open /etc/recovery.fstab (%s)\n", strerror(errno)); + fstab = fs_mgr_read_fstab("/etc/recovery.fstab"); + if (!fstab) { + LOGE("failed to read /etc/recovery.fstab\n"); return; } - char buffer[1024]; - int i; - while (fgets(buffer, sizeof(buffer)-1, fstab)) { - for (i = 0; buffer[i] && isspace(buffer[i]); ++i); - if (buffer[i] == '\0' || buffer[i] == '#') continue; - - char* original = strdup(buffer); - - char* mount_point = strtok(buffer+i, " \t\n"); - char* fs_type = strtok(NULL, " \t\n"); - char* device = strtok(NULL, " \t\n"); - // lines may optionally have a second device, to use if - // mounting the first one fails. - char* options = NULL; - char* device2 = strtok(NULL, " \t\n"); - if (device2) { - if (device2[0] == '/') { - options = strtok(NULL, " \t\n"); - } else { - options = device2; - device2 = NULL; - } - } - - if (mount_point && fs_type && device) { - while (num_volumes >= alloc) { - alloc *= 2; - device_volumes = (Volume*)realloc(device_volumes, alloc*sizeof(Volume)); - } - device_volumes[num_volumes].mount_point = strdup(mount_point); - device_volumes[num_volumes].fs_type = strdup(fs_type); - device_volumes[num_volumes].device = strdup(device); - device_volumes[num_volumes].device2 = - device2 ? strdup(device2) : NULL; - - device_volumes[num_volumes].length = 0; - if (parse_options(options, device_volumes + num_volumes) != 0) { - LOGE("skipping malformed recovery.fstab line: %s\n", original); - } else { - ++num_volumes; - } - } else { - LOGE("skipping malformed recovery.fstab line: %s\n", original); - } - free(original); + ret = fs_mgr_add_entry(fstab, "/tmp", "ramdisk", "ramdisk", 0); + if (ret < 0 ) { + LOGE("failed to add /tmp entry to fstab\n"); + fs_mgr_free_fstab(fstab); + fstab = NULL; + return; } - fclose(fstab); - printf("recovery filesystem table\n"); printf("=========================\n"); - for (i = 0; i < num_volumes; ++i) { - Volume* v = &device_volumes[i]; - printf(" %d %s %s %s %s %lld\n", i, v->mount_point, v->fs_type, - v->device, v->device2, v->length); + for (i = 0; i < fstab->num_entries; ++i) { + Volume* v = &fstab->recs[i]; + printf(" %d %s %s %s %lld\n", i, v->mount_point, v->fs_type, + v->blk_device, v->length); } printf("\n"); } Volume* volume_for_path(const char* path) { - int i; - for (i = 0; i < num_volumes; ++i) { - Volume* v = device_volumes+i; - int len = strlen(v->mount_point); - if (strncmp(path, v->mount_point, len) == 0 && - (path[len] == '\0' || path[len] == '/')) { - return v; - } - } - return NULL; + return fs_mgr_get_entry_for_mount_point(fstab, path); } int ensure_path_mounted(const char* path) { @@ -177,27 +104,19 @@ int ensure_path_mounted(const char* path) { // mount an MTD partition as a YAFFS2 filesystem. mtd_scan_partitions(); const MtdPartition* partition; - partition = mtd_find_partition_by_name(v->device); + partition = mtd_find_partition_by_name(v->blk_device); if (partition == NULL) { LOGE("failed to find \"%s\" partition to mount at \"%s\"\n", - v->device, v->mount_point); + v->blk_device, v->mount_point); return -1; } return mtd_mount_partition(partition, v->mount_point, v->fs_type, 0); } else if (strcmp(v->fs_type, "ext4") == 0 || strcmp(v->fs_type, "vfat") == 0) { - result = mount(v->device, v->mount_point, v->fs_type, + result = mount(v->blk_device, v->mount_point, v->fs_type, MS_NOATIME | MS_NODEV | MS_NODIRATIME, ""); if (result == 0) return 0; - if (v->device2) { - LOGW("failed to mount %s (%s); trying %s\n", - v->device, strerror(errno), v->device2); - result = mount(v->device2, v->mount_point, v->fs_type, - MS_NOATIME | MS_NODEV | MS_NODIRATIME, ""); - if (result == 0) return 0; - } - LOGE("failed to mount %s (%s)\n", v->mount_point, strerror(errno)); return -1; } @@ -265,38 +184,31 @@ int format_volume(const char* volume) { if (strcmp(v->fs_type, "yaffs2") == 0 || strcmp(v->fs_type, "mtd") == 0) { mtd_scan_partitions(); - const MtdPartition* partition = mtd_find_partition_by_name(v->device); + const MtdPartition* partition = mtd_find_partition_by_name(v->blk_device); if (partition == NULL) { - LOGE("format_volume: no MTD partition \"%s\"\n", v->device); + LOGE("format_volume: no MTD partition \"%s\"\n", v->blk_device); return -1; } MtdWriteContext *write = mtd_write_partition(partition); if (write == NULL) { - LOGW("format_volume: can't open MTD \"%s\"\n", v->device); + LOGW("format_volume: can't open MTD \"%s\"\n", v->blk_device); return -1; } else if (mtd_erase_blocks(write, -1) == (off_t) -1) { - LOGW("format_volume: can't erase MTD \"%s\"\n", v->device); + LOGW("format_volume: can't erase MTD \"%s\"\n", v->blk_device); mtd_write_close(write); return -1; } else if (mtd_write_close(write)) { - LOGW("format_volume: can't close MTD \"%s\"\n", v->device); + LOGW("format_volume: can't close MTD \"%s\"\n", v->blk_device); return -1; } return 0; } if (strcmp(v->fs_type, "ext4") == 0) { -#ifdef USE_EXT4 -/* - int result = make_ext4fs(v->device, v->length, volume, sehandle); -*/ - int result = 0; -#else - int result = 0; -#endif + int result = make_ext4fs(v->blk_device, v->length, volume, sehandle); if (result != 0) { - LOGE("format_volume: make_extf4fs failed on %s\n", v->device); + LOGE("format_volume: make_extf4fs failed on %s\n", v->blk_device); return -1; } return 0; diff --git a/screen_ui.cpp b/screen_ui.cpp index 4441f7abc..4a83a5c73 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -40,8 +40,8 @@ int twgr_text(int x, int y, const char *s); } #include "data.hpp" -#define CHAR_WIDTH 10 -#define CHAR_HEIGHT 18 +static int char_width; +static int char_height; // There's only (at most) one of these objects, and global callbacks // (for pthread_create, and the input event system) need to find it, @@ -58,6 +58,7 @@ static double now() { ScreenRecoveryUI::ScreenRecoveryUI() : currentIcon(NONE), installingFrame(0), + rtl_locale(false), progressBarType(EMPTY), progressScopeStart(0), progressScopeSize(0), @@ -84,7 +85,13 @@ ScreenRecoveryUI::ScreenRecoveryUI() : indeterminate_frames(6), installing_frames(7), install_overlay_offset_x(13), - install_overlay_offset_y(190) { + install_overlay_offset_y(190), + overlay_offset_x(-1), + overlay_offset_y(-1) { + + for (int i = 0; i < 5; i++) + backgroundIcon[i] = NULL; + pthread_mutex_init(&updateMutex, NULL); self = this; } @@ -95,12 +102,12 @@ ScreenRecoveryUI::ScreenRecoveryUI() : // animation. Does nothing if no overlay animation is defined. // Should only be called with updateMutex locked. void ScreenRecoveryUI::draw_install_overlay_locked(int frame) { - if (installationOverlay == NULL) return; + if (installationOverlay == NULL || overlay_offset_x < 0) return; gr_surface surface = installationOverlay[frame]; int iconWidth = gr_get_width(surface); int iconHeight = gr_get_height(surface); gr_blit(surface, 0, 0, iconWidth, iconHeight, - install_overlay_offset_x, install_overlay_offset_y); + overlay_offset_x, overlay_offset_y); } // Clear the screen and draw the currently selected background icon (if any). @@ -113,14 +120,26 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) if (icon) { gr_surface surface = backgroundIcon[icon]; + gr_surface text_surface = backgroundText[icon]; + int iconWidth = gr_get_width(surface); int iconHeight = gr_get_height(surface); + int textWidth = gr_get_width(text_surface); + int textHeight = gr_get_height(text_surface); + int iconX = (gr_fb_width() - iconWidth) / 2; - int iconY = (gr_fb_height() - iconHeight) / 2; + int iconY = (gr_fb_height() - (iconHeight+textHeight+40)) / 2; + + int textX = (gr_fb_width() - textWidth) / 2; + int textY = ((gr_fb_height() - (iconHeight+textHeight+40)) / 2) + iconHeight + 40; + gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY); - if (icon == INSTALLING) { + if (icon == INSTALLING_UPDATE || icon == ERASING) { draw_install_overlay_locked(installingFrame); } + + gr_color(255, 255, 255, 255); + gr_texticon(textX, textY, text_surface); } } @@ -130,12 +149,12 @@ void ScreenRecoveryUI::draw_progress_locked() { if (currentIcon == ERROR) return; - if (currentIcon == INSTALLING) { + if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { draw_install_overlay_locked(installingFrame); } if (progressBarType != EMPTY) { - int iconHeight = gr_get_height(backgroundIcon[INSTALLING]); + int iconHeight = gr_get_height(backgroundIcon[INSTALLING_UPDATE]); int width = gr_get_width(progressBarEmpty); int height = gr_get_height(progressBarEmpty); @@ -150,27 +169,42 @@ void ScreenRecoveryUI::draw_progress_locked() float p = progressScopeStart + progress * progressScopeSize; int pos = (int) (p * width); - if (pos > 0) { - gr_blit(progressBarFill, 0, 0, pos, height, dx, dy); - } - if (pos < width-1) { - gr_blit(progressBarEmpty, pos, 0, width-pos, height, dx+pos, dy); + if (rtl_locale) { + // Fill the progress bar from right to left. + if (pos > 0) { + gr_blit(progressBarFill, width-pos, 0, pos, height, dx+width-pos, dy); + } + if (pos < width-1) { + gr_blit(progressBarEmpty, 0, 0, width-pos, height, dx, dy); + } + } else { + // Fill the progress bar from left to right. + if (pos > 0) { + gr_blit(progressBarFill, 0, 0, pos, height, dx, dy); + } + if (pos < width-1) { + gr_blit(progressBarEmpty, pos, 0, width-pos, height, dx+pos, dy); + } } } if (progressBarType == INDETERMINATE) { static int frame = 0; gr_blit(progressBarIndeterminate[frame], 0, 0, width, height, dx, dy); - frame = (frame + 1) % indeterminate_frames; + // in RTL locales, we run the animation backwards, which + // makes the spinner spin the other way. + if (rtl_locale) { + frame = (frame + indeterminate_frames - 1) % indeterminate_frames; + } else { + frame = (frame + 1) % indeterminate_frames; + } } } } -void ScreenRecoveryUI::draw_text_line(int row, const char* t) { - if (t[0] != '\0') { - twgr_text(0, (row+1)*CHAR_HEIGHT-1, t); - } -} +#define C_HEADER 247,0,6 +#define C_MENU 0,106,157 +#define C_LOG 249,194,0 // Redraw everything on the screen. Does not flip pages. // Should only be called with updateMutex locked. @@ -183,30 +217,46 @@ void ScreenRecoveryUI::draw_screen_locked() gr_color(0, 0, 0, 160); gr_fill(0, 0, gr_fb_width(), gr_fb_height()); + int y = 0; int i = 0; if (show_menu) { - gr_color(64, 96, 255, 255); - gr_fill(0, (menu_top+menu_sel) * CHAR_HEIGHT, - gr_fb_width(), (menu_top+menu_sel+1)*CHAR_HEIGHT+1); + gr_color(C_HEADER, 255); for (; i < menu_top + menu_items; ++i) { + if (i == menu_top) gr_color(C_MENU, 255); + if (i == menu_top + menu_sel) { + // draw the highlight bar + gr_fill(0, y-2, gr_fb_width(), y+char_height+2); + // white text of selected item gr_color(255, 255, 255, 255); - draw_text_line(i, menu[i]); - gr_color(64, 96, 255, 255); + if (menu[i][0]) gr_text(4, y, menu[i], 1); + gr_color(C_MENU, 255); } else { - draw_text_line(i, menu[i]); + if (menu[i][0]) gr_text(4, y, menu[i], i < menu_top); } + y += char_height+4; } - gr_fill(0, i*CHAR_HEIGHT+CHAR_HEIGHT/2-1, - gr_fb_width(), i*CHAR_HEIGHT+CHAR_HEIGHT/2+1); + gr_color(C_MENU, 255); + y += 4; + gr_fill(0, y, gr_fb_width(), y+2); + y += 4; ++i; } - gr_color(255, 255, 0, 255); - - for (; i < text_rows; ++i) { - draw_text_line(i, text[(i+text_top) % text_rows]); + gr_color(C_LOG, 255); + + // display from the bottom up, until we hit the top of the + // screen, the bottom of the menu, or we've displayed the + // entire text buffer. + int ty; + int row = (text_top+text_rows-1) % text_rows; + for (int ty = gr_fb_height() - char_height, count = 0; + ty > y+2 && count < text_rows; + ty -= char_height, ++count) { + gr_text(4, ty, text[row], 0); + --row; + if (row < 0) row = text_rows-1; } } } @@ -248,7 +298,8 @@ void ScreenRecoveryUI::progress_loop() { // update the installation animation, if active // skip this if we have a text overlay (too expensive to update) - if (currentIcon == INSTALLING && installing_frames > 0 && !show_text) { + if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) && + installing_frames > 0 && !show_text) { installingFrame = (installingFrame + 1) % installing_frames; redraw = 1; } @@ -289,23 +340,40 @@ void ScreenRecoveryUI::LoadBitmap(const char* filename, gr_surface* surface) { } } +void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, gr_surface* surface) { + int result = res_create_localized_surface(filename, surface); + if (result < 0) { + LOGE("missing bitmap %s\n(Code %d)\n", filename, result); + } +} + void ScreenRecoveryUI::Init() { gr_init(); + gr_font_size(&char_width, &char_height); + text_col = text_row = 0; - text_rows = gr_fb_height() / CHAR_HEIGHT; + text_rows = gr_fb_height() / char_height; if (text_rows > kMaxRows) text_rows = kMaxRows; text_top = 1; - text_cols = gr_fb_width() / CHAR_WIDTH; + text_cols = gr_fb_width() / char_width; if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1; - LoadBitmap("icon_installing", &backgroundIcon[INSTALLING]); + LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]); + backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE]; LoadBitmap("icon_error", &backgroundIcon[ERROR]); + backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR]; + LoadBitmap("progress_empty", &progressBarEmpty); LoadBitmap("progress_fill", &progressBarFill); + LoadLocalizedBitmap("installing_text", &backgroundText[INSTALLING_UPDATE]); + LoadLocalizedBitmap("erasing_text", &backgroundText[ERASING]); + LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]); + LoadLocalizedBitmap("error_text", &backgroundText[ERROR]); + int i; progressBarIndeterminate = (gr_surface*)malloc(indeterminate_frames * @@ -327,14 +395,6 @@ void ScreenRecoveryUI::Init() sprintf(filename, "icon_installing_overlay%02d", i+1); LoadBitmap(filename, installationOverlay+i); } - - // Adjust the offset to account for the positioning of the - // base image on the screen. - if (backgroundIcon[INSTALLING] != NULL) { - gr_surface bg = backgroundIcon[INSTALLING]; - install_overlay_offset_x += (gr_fb_width() - gr_get_width(bg)) / 2; - install_overlay_offset_y += (gr_fb_height() - gr_get_height(bg)) / 2; - } } else { installationOverlay = NULL; } @@ -344,11 +404,46 @@ void ScreenRecoveryUI::Init() RecoveryUI::Init(); } +void ScreenRecoveryUI::SetLocale(const char* locale) { + if (locale) { + char* lang = strdup(locale); + for (char* p = lang; *p; ++p) { + if (*p == '_') { + *p = '\0'; + break; + } + } + + // A bit cheesy: keep an explicit list of supported languages + // that are RTL. + if (strcmp(lang, "ar") == 0 || // Arabic + strcmp(lang, "fa") == 0 || // Persian (Farsi) + strcmp(lang, "he") == 0 || // Hebrew (new language code) + strcmp(lang, "iw") == 0 || // Hebrew (old language code) + strcmp(lang, "ur") == 0) { // Urdu + rtl_locale = true; + } + free(lang); + } +} + void ScreenRecoveryUI::SetBackground(Icon icon) { pthread_mutex_lock(&updateMutex); + + // Adjust the offset to account for the positioning of the + // base image on the screen. + if (backgroundIcon[icon] != NULL) { + gr_surface bg = backgroundIcon[icon]; + gr_surface text = backgroundText[icon]; + overlay_offset_x = install_overlay_offset_x + (gr_fb_width() - gr_get_width(bg)) / 2; + overlay_offset_y = install_overlay_offset_y + + (gr_fb_height() - (gr_get_height(bg) + gr_get_height(text) + 40)) / 2; + } + currentIcon = icon; update_screen_locked(); + pthread_mutex_unlock(&updateMutex); } diff --git a/screen_ui.h b/screen_ui.h index 34929ee1a..fe0de46e8 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -29,6 +29,7 @@ class ScreenRecoveryUI : public RecoveryUI { ScreenRecoveryUI(); void Init(); + void SetLocale(const char* locale); // overall recovery state ("background image") void SetBackground(Icon icon); @@ -55,9 +56,11 @@ class ScreenRecoveryUI : public RecoveryUI { private: Icon currentIcon; int installingFrame; + bool rtl_locale; pthread_mutex_t updateMutex; - gr_surface backgroundIcon[3]; + gr_surface backgroundIcon[5]; + gr_surface backgroundText[5]; gr_surface *installationOverlay; gr_surface *progressBarIndeterminate; gr_surface progressBarEmpty; @@ -73,7 +76,7 @@ class ScreenRecoveryUI : public RecoveryUI { bool pagesIdentical; static const int kMaxCols = 96; - static const int kMaxRows = 32; + static const int kMaxRows = 96; // Log text overlay, displayed when a magic key is pressed char text[kMaxRows][kMaxCols]; @@ -92,11 +95,11 @@ class ScreenRecoveryUI : public RecoveryUI { int indeterminate_frames; int installing_frames; int install_overlay_offset_x, install_overlay_offset_y; + int overlay_offset_x, overlay_offset_y; void draw_install_overlay_locked(int frame); void draw_background_locked(Icon icon); void draw_progress_locked(); - void draw_text_line(int row, const char* t); void draw_screen_locked(); void update_screen_locked(); void update_progress_locked(); @@ -104,7 +107,7 @@ class ScreenRecoveryUI : public RecoveryUI { void progress_loop(); void LoadBitmap(const char* filename, gr_surface* surface); - + void LoadLocalizedBitmap(const char* filename, gr_surface* surface); }; #endif // RECOVERY_UI_H diff --git a/testdata/otasigned_f4.zip b/testdata/otasigned_f4.zip Binary files differnew file mode 100644 index 000000000..dd1e4dd40 --- /dev/null +++ b/testdata/otasigned_f4.zip diff --git a/testdata/test_f4.pk8 b/testdata/test_f4.pk8 Binary files differnew file mode 100644 index 000000000..3052613c5 --- /dev/null +++ b/testdata/test_f4.pk8 diff --git a/testdata/test_f4.x509.pem b/testdata/test_f4.x509.pem new file mode 100644 index 000000000..814abcf99 --- /dev/null +++ b/testdata/test_f4.x509.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIJAKhkCO1dDYMaMA0GCSqGSIb3DQEBBQUAMG8xCzAJBgNV +BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBW +aWV3MQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMT +B1Rlc3QxMjMwHhcNMTIwNzI1MTg1NzAzWhcNMzkxMjExMTg1NzAzWjBvMQswCQYD +VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g +VmlldzEPMA0GA1UEChMGR29vZ2xlMRAwDgYDVQQLEwdBbmRyb2lkMRAwDgYDVQQD +EwdUZXN0MTIzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu8WwMN9x +4Mz7YgkG2qy9g8/kl5ZoYrUM0ApHhaITAcL7RXLZaNipCf0w/YjYTQgj+75MK30x +TsnPeWNOEwA62gkHrZyyWfxBRO6kBYuIuI4roGDBJOmKQ1OEaDeIRKu7q5V8v3Cs +0wQDAQWTbhpxBZr9UYFgJUg8XWBfPrGJLVwsoiy4xrMhoTlNZKHfwOMMqVtSHkZX +qydYrcIzyjh+TO0e/xSNQ8MMRRbtqWgCHN6Rzhog3IHZu0RaPoukariopjXM/s0V +gTm3rHDHCOpna2pNblyiFlvbkoCs769mtNmx/yrDShO30jg/xaG8RypKDvTChzOT +oWW/XQ5VEXjbHwIDAQABo4HUMIHRMB0GA1UdDgQWBBRlT2dEZJY1tmUM8mZ0xnhS +GdD9TTCBoQYDVR0jBIGZMIGWgBRlT2dEZJY1tmUM8mZ0xnhSGdD9TaFzpHEwbzEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50 +YWluIFZpZXcxDzANBgNVBAoTBkdvb2dsZTEQMA4GA1UECxMHQW5kcm9pZDEQMA4G +A1UEAxMHVGVzdDEyM4IJAKhkCO1dDYMaMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN +AQEFBQADggEBAHqnXHtE+h3hvGmHh24GT51vGAYLc68WUUtCVlMIU85zQ757wlxZ +BmRypZ1i9hSqnXj5n+mETV5rFX3g2gvdAPVHkRycuDa2aUdZSE8cW4Z6qYFx6SaD +e+3SyXokpUquW64RuHJrf/yd/FnGjneBe3Qpm2reuzGWNH90qZGdbsfNaCm5kx2L +X+ZNHM3CcGMLaphY5++sM0JxSEcju5EK33ZYgLf4YdlbyMp8LDFVNd7ff0SFi9fF +0ZlAsJWoS3QmVCj2744BFdsCu7UHpnYpG6X3MT4SHAawdOaT5zSuaCl2xx6H0O7t +w/Fvbl/KVD1ZmLHgBKjDMNSh0OB9mSsDWpw= +-----END CERTIFICATE----- @@ -120,6 +120,18 @@ int main(int argc, char **argv) { bool Cache_Wipe = false, Factory_Reset = false, Perform_Backup = false; { + TWPartition* misc = PartitionManager.Find_Partition_By_Path("/misc"); + if (misc != NULL) { + if (misc->Current_File_System == "emmc") { + set_device_type('e'); + set_device_name(misc->Actual_Block_Device.c_str()); + } else if (misc->Current_File_System == "mtd") { + set_device_type('m'); + set_device_name(misc->MTD_Name.c_str()); + } else { + LOGERR("Unknown file system for /misc\n"); + } + } get_args(&argc, &argv); int index, index2, len; @@ -47,7 +47,8 @@ static RecoveryUI* self = NULL; RecoveryUI::RecoveryUI() : key_queue_len(0), - key_last_down(-1) { + key_last_down(-1), + key_down_time(0) { pthread_mutex_init(&key_queue_mutex, NULL); pthread_cond_init(&key_queue_cond, NULL); self = this; @@ -111,19 +112,29 @@ int RecoveryUI::input_callback(int fd, short revents, void* data) // updown == 1 for key down events; 0 for key up events void RecoveryUI::process_key(int key_code, int updown) { bool register_key = false; + bool long_press = false; + + const long long_threshold = CLOCKS_PER_SEC * 750 / 1000; pthread_mutex_lock(&key_queue_mutex); key_pressed[key_code] = updown; if (updown) { key_last_down = key_code; + key_down_time = clock(); } else { - if (key_last_down == key_code) + if (key_last_down == key_code) { + long duration = clock() - key_down_time; + if (duration > long_threshold) { + long_press = true; + } register_key = true; + } key_last_down = -1; } pthread_mutex_unlock(&key_queue_mutex); if (register_key) { + NextCheckKeyIsLong(long_press); switch (CheckKey(key_code)) { case RecoveryUI::IGNORE: break; @@ -139,18 +150,23 @@ void RecoveryUI::process_key(int key_code, int updown) { break; case RecoveryUI::ENQUEUE: - pthread_mutex_lock(&key_queue_mutex); - const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]); - if (key_queue_len < queue_max) { - key_queue[key_queue_len++] = key_code; - pthread_cond_signal(&key_queue_cond); - } - pthread_mutex_unlock(&key_queue_mutex); + EnqueueKey(key_code); break; } } } +void RecoveryUI::EnqueueKey(int key_code) { + pthread_mutex_lock(&key_queue_mutex); + const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]); + if (key_queue_len < queue_max) { + key_queue[key_queue_len++] = key_code; + pthread_cond_signal(&key_queue_cond); + } + pthread_mutex_unlock(&key_queue_mutex); +} + + // Reads input events, handles special hot keys, and adds to the key queue. void* RecoveryUI::input_thread(void *cookie) { @@ -227,3 +243,6 @@ void RecoveryUI::FlushKeys() { RecoveryUI::KeyAction RecoveryUI::CheckKey(int key) { return RecoveryUI::ENQUEUE; } + +void RecoveryUI::NextCheckKeyIsLong(bool is_long_press) { +} @@ -19,6 +19,7 @@ #include <linux/input.h> #include <pthread.h> +#include <time.h> // Abstract class for controlling the user interface during recovery. class RecoveryUI { @@ -30,8 +31,11 @@ class RecoveryUI { // Initialize the object; called before anything else. virtual void Init(); + // After calling Init(), you can tell the UI what locale it is operating in. + virtual void SetLocale(const char* locale) { } + // Set the overall recovery state ("background image"). - enum Icon { NONE, INSTALLING, ERROR }; + enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR }; virtual void SetBackground(Icon icon) = 0; // --- progress indicator --- @@ -76,6 +80,8 @@ class RecoveryUI { enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE }; virtual KeyAction CheckKey(int key); + virtual void NextCheckKeyIsLong(bool is_long_press); + // --- menu display --- // Display some header text followed by a menu of items, which appears @@ -92,6 +98,9 @@ class RecoveryUI { // statements will be displayed. virtual void EndMenu() = 0; +protected: + void EnqueueKey(int key_code); + private: // Key event input queue pthread_mutex_t key_queue_mutex; @@ -99,6 +108,7 @@ private: int key_queue[256], key_queue_len; char key_pressed[KEY_MAX + 1]; // under key_queue_mutex int key_last_down; // under key_queue_mutex + clock_t key_down_time; // under key_queue_mutex int rel_sum; pthread_t input_t; diff --git a/updater/Android.mk b/updater/Android.mk index c0876862a..3b883e4c7 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -32,18 +32,13 @@ LOCAL_STATIC_LIBRARIES = \ endif endif -ifeq ($(HAVE_SELINUX), true) -LOCAL_C_INCLUDES += external/libselinux/include -LOCAL_STATIC_LIBRARIES += libselinux -LOCAL_CFLAGS += -DHAVE_SELINUX -endif # HAVE_SELINUX - LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS) LOCAL_STATIC_LIBRARIES += libapplypatch libedify libmtdutils libminzip libz LOCAL_STATIC_LIBRARIES += libflashutils libmmcutils libbmlutils LOCAL_STATIC_LIBRARIES += libmincrypt libbz LOCAL_STATIC_LIBRARIES += libminelf -LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc +LOCAL_STATIC_LIBRARIES += libcutils liblog libstdc++ libc +LOCAL_STATIC_LIBRARIES += libselinux 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 c5aa77c49..305703cd3 100644 --- a/updater/install.c +++ b/updater/install.c @@ -78,23 +78,19 @@ Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { goto done; } -#ifdef HAVE_SELINUX char *secontext = NULL; if (sehandle) { selabel_lookup(sehandle, &secontext, mount_point, 0755); setfscreatecon(secontext); } -#endif mkdir(mount_point, 0755); -#ifdef HAVE_SELINUX if (secontext) { freecon(secontext); setfscreatecon(NULL); } -#endif if (strcmp(partition_type, "MTD") == 0) { mtd_scan_partitions(); @@ -456,6 +452,26 @@ Value* PackageExtractFileFn(const char* name, State* state, } } +// Create all parent directories of name, if necessary. +static int make_parents(char* name) { + char* p; + for (p = name + (strlen(name)-1); p > name; --p) { + if (*p != '/') continue; + *p = '\0'; + if (make_parents(name) < 0) return -1; + int result = mkdir(name, 0700); + if (result == 0) fprintf(stderr, "symlink(): created [%s]\n", name); + *p = '/'; + if (result == 0 || errno == EEXIST) { + // successfully created or already existed; we're done + return 0; + } else { + fprintf(stderr, "failed to mkdir %s: %s\n", name, strerror(errno)); + return -1; + } + } + return 0; +} // symlink target src1 src2 ... // unlinks any previously existing src1, src2, etc before creating symlinks. @@ -483,6 +499,11 @@ Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { ++bad; } } + if (make_parents(srcs[i])) { + fprintf(stderr, "%s: failed to symlink %s to %s: making parents failed\n", + name, srcs[i], target); + ++bad; + } if (symlink(target, srcs[i]) < 0) { fprintf(stderr, "%s: failed to symlink %s to %s: %s\n", name, srcs[i], target, strerror(errno)); @@ -504,7 +525,8 @@ Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { int min_args = 4 + (recursive ? 1 : 0); if (argc < min_args) { - return ErrorAbort(state, "%s() expects %d+ args, got %d", name, argc); + return ErrorAbort(state, "%s() expects %d+ args, got %d", + name, min_args, argc); } char** args = ReadVarArgs(state, argc, argv); @@ -626,7 +648,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 %d bytes", name, st.st_size+1); + ErrorAbort(state, "%s: failed to alloc %lld bytes", name, st.st_size+1); goto done; } @@ -638,7 +660,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 %d bytes from %s", + ErrorAbort(state, "%s: failed to read %lld bytes from %s", name, st.st_size+1, filename); fclose(f); goto done; @@ -823,7 +845,7 @@ Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { int result = applypatch(source_filename, target_filename, target_sha1, target_size, - patchcount, patch_sha_str, patches); + patchcount, patch_sha_str, patches, NULL); for (i = 0; i < patchcount; ++i) { FreeValue(patches[i]); diff --git a/updater/updater.c b/updater/updater.c index 5f1580870..58ac27f9e 100644 --- a/updater/updater.c +++ b/updater/updater.c @@ -105,7 +105,6 @@ int main(int argc, char** argv) { return 6; } -#ifdef HAVE_SELINUX struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "/file_contexts" } }; @@ -116,7 +115,6 @@ int main(int argc, char** argv) { fprintf(stderr, "Warning: No file_contexts\n"); fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n"); } -#endif // Evaluate the parsed script. diff --git a/updater/updater.h b/updater/updater.h index a00872ca4..d2e901141 100644 --- a/updater/updater.h +++ b/updater/updater.h @@ -20,12 +20,8 @@ #include <stdio.h> #include "minzip/Zip.h" -#ifdef HAVE_SELINUX #include <selinux/selinux.h> #include <selinux/label.h> -#else -struct selabel_handle; -#endif typedef struct { FILE* cmd_pipe; diff --git a/verifier.cpp b/verifier.cpp index 82739f339..a93e8d18b 100644 --- a/verifier.cpp +++ b/verifier.cpp @@ -29,82 +29,6 @@ #define PUBLIC_KEYS_FILE "/res/keys" -// Reads a file containing one or more public keys as produced by -// DumpPublicKey: this is an RSAPublicKey struct as it would appear -// as a C source literal, eg: -// -// "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}" -// -// (Note that the braces and commas in this example are actual -// characters the parser expects to find in the file; the ellipses -// indicate more numbers omitted from this example.) -// -// The file may contain multiple keys in this format, separated by -// commas. The last key must not be followed by a comma. -// -// Returns NULL if the file failed to parse, or if it contain zero keys. -static RSAPublicKey* -load_keys(const char* filename, int* numKeys) { - RSAPublicKey* out = NULL; - *numKeys = 0; - - FILE* f = fopen(filename, "r"); - if (f == NULL) { - printf("opening %s: %s\n", filename, strerror(errno)); - goto exit; - } - - { - int i; - bool done = false; - while (!done) { - ++*numKeys; - out = (RSAPublicKey*)realloc(out, *numKeys * sizeof(RSAPublicKey)); - RSAPublicKey* key = out + (*numKeys - 1); - if (fscanf(f, " { %i , 0x%x , { %u", - &(key->len), &(key->n0inv), &(key->n[0])) != 3) { - goto exit; - } - if (key->len != RSANUMWORDS) { - printf("key length (%d) does not match expected size\n", key->len); - goto exit; - } - for (i = 1; i < key->len; ++i) { - if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit; - } - if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit; - for (i = 1; i < key->len; ++i) { - if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit; - } - fscanf(f, " } } "); - - // if the line ends in a comma, this file has more keys. - switch (fgetc(f)) { - case ',': - // more keys to come. - break; - - case EOF: - done = true; - break; - - default: - printf("unexpected character between keys\n"); - goto exit; - } - } - } - - fclose(f); - return out; - -exit: - if (f) fclose(f); - free(out); - *numKeys = 0; - return NULL; -} - // Look for an RSA signature embedded in the .ZIP file comment given // the path to the zip. Verify it matches one of the given public // keys. @@ -120,6 +44,7 @@ int verify_file(const char* path) { LOGE("Failed to load keys\n"); return VERIFY_FAILURE; } + /* LOGI("%d key(s) loaded from %s\n\n RSA Key:\n\n", numKeys, PUBLIC_KEYS_FILE); int rsa_size = sizeof(RSAPublicKey); unsigned char* ptr = (unsigned char*) loadedKeys; @@ -129,7 +54,7 @@ int verify_file(const char* path) { printf("%02x ", valuedees); ptr++; } - printf("\n\n"); + printf("\n\n");*/ FILE* f = fopen(path, "rb"); if (f == NULL) { @@ -274,6 +199,8 @@ int verify_file(const char* path) { LOGI("whole-file signature verified against key %d\n", i); free(eocd); return VERIFY_SUCCESS; + } else { + LOGI("failed to verify against key %d\n", i); } LOGI("i: %i, eocd_size: %i, RSANUMBYTES: %i, returned %i\n", i, eocd_size, RSANUMBYTES, dees); } @@ -281,3 +208,108 @@ int verify_file(const char* path) { LOGE("failed to verify whole-file signature\n"); return VERIFY_FAILURE; } + +// Reads a file containing one or more public keys as produced by +// DumpPublicKey: this is an RSAPublicKey struct as it would appear +// as a C source literal, eg: +// +// "{64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}" +// +// For key versions newer than the original 2048-bit e=3 keys +// supported by Android, the string is preceded by a version +// identifier, eg: +// +// "v2 {64,0xc926ad21,{1795090719,...,-695002876},{-857949815,...,1175080310}}" +// +// (Note that the braces and commas in this example are actual +// characters the parser expects to find in the file; the ellipses +// indicate more numbers omitted from this example.) +// +// The file may contain multiple keys in this format, separated by +// commas. The last key must not be followed by a comma. +// +// Returns NULL if the file failed to parse, or if it contain zero keys. +RSAPublicKey* +load_keys(const char* filename, int* numKeys) { + RSAPublicKey* out = NULL; + *numKeys = 0; + + FILE* f = fopen(filename, "r"); + if (f == NULL) { + LOGE("opening %s: %s\n", filename, strerror(errno)); + goto exit; + } + + { + int i; + bool done = false; + while (!done) { + ++*numKeys; + out = (RSAPublicKey*)realloc(out, *numKeys * sizeof(RSAPublicKey)); + RSAPublicKey* key = out + (*numKeys - 1); + +#ifdef HAS_EXPONENT + char start_char; + if (fscanf(f, " %c", &start_char) != 1) goto exit; + if (start_char == '{') { + // a version 1 key has no version specifier. + key->exponent = 3; + } else if (start_char == 'v') { + int version; + if (fscanf(f, "%d {", &version) != 1) goto exit; + if (version == 2) { + key->exponent = 65537; + } else { + goto exit; + } + } + + if (fscanf(f, " %i , 0x%x , { %u", +#else + if (fscanf(f, " { %i , 0x%x , { %u", +#endif + &(key->len), &(key->n0inv), &(key->n[0])) != 3) { + goto exit; + } + if (key->len != RSANUMWORDS) { + LOGE("key length (%d) does not match expected size\n", key->len); + goto exit; + } + for (i = 1; i < key->len; ++i) { + if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit; + } + if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit; + for (i = 1; i < key->len; ++i) { + if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit; + } + fscanf(f, " } } "); + + // if the line ends in a comma, this file has more keys. + switch (fgetc(f)) { + case ',': + // more keys to come. + break; + + case EOF: + done = true; + break; + + default: + LOGE("unexpected character between keys\n"); + goto exit; + } +#ifdef HAS_EXPONENT + LOGI("read key e=%d\n", key->exponent); +#endif + } + } + + fclose(f); + return out; + +exit: + if (f) fclose(f); + free(out); + *numKeys = 0; + return NULL; +} diff --git a/verifier.h b/verifier.h index c5a2391c7..b355b3eb7 100644 --- a/verifier.h +++ b/verifier.h @@ -30,6 +30,8 @@ static const float VERIFICATION_PROGRESS_FRACTION = 0.25; */ int verify_file(const char* path); +RSAPublicKey* load_keys(const char* filename, int* numKeys); + #define VERIFY_SUCCESS 0 #define VERIFY_FAILURE 1 diff --git a/verifier_test.cpp b/verifier_test.cpp index b263db808..2ef52a0f7 100644 --- a/verifier_test.cpp +++ b/verifier_test.cpp @@ -18,6 +18,7 @@ #include <stdlib.h> #include <stdarg.h> +#include "common.h" #include "verifier.h" #include "ui.h" @@ -56,7 +57,45 @@ RSAPublicKey test_key = 9135381, 1625809335, -1490225159, -1342673351, 1117190829, -57654514, 1825108855, -1281819325, 1111251351, -1726129724, 1684324211, -1773988491, - 367251975, 810756730, -1941182952, 1175080310 } + 367251975, 810756730, -1941182952, 1175080310 }, + 3 + }; + +RSAPublicKey test_f4_key = + { 64, 0xc9bd1f21, + { 293133087u, 3210546773u, 865313125u, 250921607u, + 3158780490u, 943703457u, 1242806226u, 2986289859u, + 2942743769u, 2457906415u, 2719374299u, 1783459420u, + 149579627u, 3081531591u, 3440738617u, 2788543742u, + 2758457512u, 1146764939u, 3699497403u, 2446203424u, + 1744968926u, 1159130537u, 2370028300u, 3978231572u, + 3392699980u, 1487782451u, 1180150567u, 2841334302u, + 3753960204u, 961373345u, 3333628321u, 748825784u, + 2978557276u, 1566596926u, 1613056060u, 2600292737u, + 1847226629u, 50398611u, 1890374404u, 2878700735u, + 2286201787u, 1401186359u, 619285059u, 731930817u, + 2340993166u, 1156490245u, 2992241729u, 151498140u, + 318782170u, 3480838990u, 2100383433u, 4223552555u, + 3628927011u, 4247846280u, 1759029513u, 4215632601u, + 2719154626u, 3490334597u, 1751299340u, 3487864726u, + 3668753795u, 4217506054u, 3748782284u, 3150295088u }, + { 1772626313u, 445326068u, 3477676155u, 1758201194u, + 2986784722u, 491035581u, 3922936562u, 702212696u, + 2979856666u, 3324974564u, 2488428922u, 3056318590u, + 1626954946u, 664714029u, 398585816u, 3964097931u, + 3356701905u, 2298377729u, 2040082097u, 3025491477u, + 539143308u, 3348777868u, 2995302452u, 3602465520u, + 212480763u, 2691021393u, 1307177300u, 704008044u, + 2031136606u, 1054106474u, 3838318865u, 2441343869u, + 1477566916u, 700949900u, 2534790355u, 3353533667u, + 336163563u, 4106790558u, 2701448228u, 1571536379u, + 1103842411u, 3623110423u, 1635278839u, 1577828979u, + 910322800u, 715583630u, 138128831u, 1017877531u, + 2289162787u, 447994798u, 1897243165u, 4121561445u, + 4150719842u, 2131821093u, 2262395396u, 3305771534u, + 980753571u, 3256525190u, 3128121808u, 1072869975u, + 3507939515u, 4229109952u, 118381341u, 2209831334u }, + 65537 }; RecoveryUI* ui = NULL; @@ -75,13 +114,10 @@ class FakeUI : public RecoveryUI { bool IsTextVisible() { return false; } bool WasTextEverVisible() { return false; } void Print(const char* fmt, ...) { - char buf[256]; va_list ap; va_start(ap, fmt); - vsnprintf(buf, 256, fmt, ap); + vfprintf(stderr, fmt, ap); va_end(ap); - - fputs(buf, stderr); } void StartMenu(const char* const * headers, const char* const * items, @@ -90,15 +126,35 @@ class FakeUI : public RecoveryUI { void EndMenu() { } }; +void +ui_print(const char* format, ...) { + va_list ap; + va_start(ap, format); + vfprintf(stdout, format, ap); + va_end(ap); +} + int main(int argc, char **argv) { - if (argc != 2) { - fprintf(stderr, "Usage: %s <package>\n", argv[0]); + if (argc < 2 || argc > 4) { + fprintf(stderr, "Usage: %s [-f4 | -file <keys>] <package>\n", argv[0]); return 2; } + RSAPublicKey* key = &test_key; + int num_keys = 1; + ++argv; + if (strcmp(argv[0], "-f4") == 0) { + ++argv; + key = &test_f4_key; + } else if (strcmp(argv[0], "-file") == 0) { + ++argv; + key = load_keys(argv[0], &num_keys); + ++argv; + } + ui = new FakeUI(); - int result = verify_file(argv[1]); + int result = verify_file(*argv, key, num_keys); if (result == VERIFY_SUCCESS) { printf("SUCCESS\n"); return 0; diff --git a/verifier_test.sh b/verifier_test.sh index a1de5c57b..378b0e5ff 100755 --- a/verifier_test.sh +++ b/verifier_test.sh @@ -73,9 +73,24 @@ expect_fail() { run_command $WORK_DIR/verifier_test $WORK_DIR/package.zip && fail } +expect_succeed_f4() { + testname "$1 (should succeed)" + $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip + run_command $WORK_DIR/verifier_test -f4 $WORK_DIR/package.zip || fail +} + +expect_fail_f4() { + testname "$1 (should fail)" + $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip + run_command $WORK_DIR/verifier_test -f4 $WORK_DIR/package.zip && fail +} + expect_fail unsigned.zip expect_fail jarsigned.zip expect_succeed otasigned.zip +expect_fail_f4 otasigned.zip +expect_succeed_f4 otasigned_f4.zip +expect_fail otasigned_f4.zip expect_fail random.zip expect_fail fake-eocd.zip expect_fail alter-metadata.zip |