From 5f05ac7c88a7b9c964f922bb4da5d551b545d4c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thiago=20Vin=C3=ADcius=20Freire=20de=20Ara=C3=BAjo=20Ribei?= =?UTF-8?q?ro?= Date: Sun, 19 May 2013 19:08:06 -0300 Subject: [PATCH 001/104] recovery: Fix adb backup Using http://goo.gl/6AQ3u might fail. The resulting backup consists of a tarball of the filesystem being backed up, but it might end up truncated, for reasons yet unknown. Issuing adb backup data, for instance, causes recovery to call tar, streaming it's stdout through a socket . The adb client reads from and copies what it to the final backup file. Somehow, sometimes, not all the content generated at is read at the adb client, which causes a truncated tar file to result. I'm unsure why this happens: Doesn't get flushed before is closed? Or is it that is closed with outstanding data in its buffers, which never make it to the other side? Either calling sync() or sleep() seems to remedy the issue, but I'm not sure if sync() has any effect on sockets or pipes. I also understand that sleeping for some time is not the best solution to a race condition. Therefore, I'm adding calls to sync() and sleep() and waiting for reviews from someone who knows better how these calls work. Patch set 2: Remove sync() call Change-Id: If4733066fdad809eb73e87c670f37acc4debe82d --- nandroid.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nandroid.c b/nandroid.c index 1e2adbf3f..2f872d15c 100644 --- a/nandroid.c +++ b/nandroid.c @@ -868,7 +868,9 @@ int bu_main(int argc, char** argv) { } // fprintf(stderr, "%d %d %s\n", fd, STDOUT_FILENO, argv[3]); - return nandroid_dump(partition); + int ret = nandroid_dump(partition); + sleep(10); + return ret; } else if (strcmp(argv[2], "restore") == 0) { if (argc != 3) { From 9de39c14dc5d45e87b627f26853c98e595ddcd74 Mon Sep 17 00:00:00 2001 From: Matt Mower Date: Fri, 24 May 2013 10:02:12 -0500 Subject: [PATCH 002/104] Remove (un)mount option for datamedia partitions When /sdcard is /data/media, it can be formatted but it cannot be mounted/unmounted independently of /data (aside from creating or destrying the symlink). This removes /sdcard from the mountable volumes list, but leaves it as a formattable partition. Change-Id: If59e4516a12da4589ffb0dbd19702f219260370e --- extendedcommands.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/extendedcommands.c b/extendedcommands.c index 6ee9c9d96..413e14cdc 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -868,10 +868,12 @@ void show_partition_menu() for (i = 0; i < num_volumes; ++i) { Volume* v = &device_volumes[i]; if(strcmp("ramdisk", v->fs_type) != 0 && strcmp("mtd", v->fs_type) != 0 && strcmp("emmc", v->fs_type) != 0 && strcmp("bml", v->fs_type) != 0) { - sprintf(&mount_menu[mountable_volumes].mount, "mount %s", v->mount_point); - sprintf(&mount_menu[mountable_volumes].unmount, "unmount %s", v->mount_point); - mount_menu[mountable_volumes].v = &device_volumes[i]; - ++mountable_volumes; + if (strcmp("datamedia", v->fs_type) != 0) { + sprintf(&mount_menu[mountable_volumes].mount, "mount %s", v->mount_point); + sprintf(&mount_menu[mountable_volumes].unmount, "unmount %s", v->mount_point); + mount_menu[mountable_volumes].v = &device_volumes[i]; + ++mountable_volumes; + } if (is_safe_to_format(v->mount_point)) { sprintf(&format_menu[formatable_volumes].txt, "format %s", v->mount_point); format_menu[formatable_volumes].v = &device_volumes[i]; From 2162ee6da07785ae8e857eb0d829f3855037e0b8 Mon Sep 17 00:00:00 2001 From: Matt Mower Date: Fri, 24 May 2013 14:25:31 -0500 Subject: [PATCH 003/104] Fix compiler warning: redefinition in extendedcommands.c ITEM_APPLY_SDCARD is already defined in recovery_ui.h, fortunately also as 1. This suggestion comes from Phil3759. Change-Id: I9ac370ca9640d61b6b96f60954d35f7aa458d75b --- extendedcommands.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extendedcommands.c b/extendedcommands.c index 6ee9c9d96..9d9faaf08 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -119,7 +119,7 @@ int install_zip(const char* packagefilepath) } #define ITEM_CHOOSE_ZIP 0 -#define ITEM_APPLY_SDCARD 1 +#define ITEM_APPLY_UPDATE 1 #define ITEM_SIG_CHECK 2 #define ITEM_CHOOSE_ZIP_INT 3 @@ -154,7 +154,7 @@ void show_install_update_menu() case ITEM_SIG_CHECK: toggle_signature_check(); break; - case ITEM_APPLY_SDCARD: + case ITEM_APPLY_UPDATE: { if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip")) install_zip(SDCARD_UPDATE_FILE); From cc5591a83af14c5274065c219af54a995557ce5c Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 25 Jul 2012 13:10:58 -0700 Subject: [PATCH 004/104] support version 2 (2048-bit e=65537) keys in recovery Signed-off-by: Chirayu Desai Change-Id: I9849c69777d513bb12926c8c622d1c12d2da568a --- install.c | 6 ++++++ verifier_test.c | 53 ++++++++++++++++++++++++++++++++++++++++++++---- verifier_test.sh | 23 +++++++++++++++------ 3 files changed, 72 insertions(+), 10 deletions(-) diff --git a/install.c b/install.c index 8b07b168f..a6a0cf104 100644 --- a/install.c +++ b/install.c @@ -266,6 +266,12 @@ try_update_binary(const char *path, ZipArchive *zip) { // // "{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.) diff --git a/verifier_test.c b/verifier_test.c index 5b6c1f451..b303ede6f 100644 --- a/verifier_test.c +++ b/verifier_test.c @@ -55,7 +55,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 }; void ui_print(const char* fmt, ...) { @@ -72,12 +110,19 @@ void ui_set_progress(float fraction) { } int main(int argc, char **argv) { - if (argc != 2) { - fprintf(stderr, "Usage: %s \n", argv[0]); + if (argc != 2 && argc != 3) { + fprintf(stderr, "Usage: %s [-f4] \n", argv[0]); return 2; } - int result = verify_file(argv[1], &test_key, 1); + RSAPublicKey* key = &test_key; + ++argv; + if (strcmp(argv[0], "-f4") == 0) { + ++argv; + key = &test_f4_key; + } + + int result = verify_file(*argv, key, 1); if (result == VERIFY_SUCCESS) { printf("SUCCESS\n"); return 0; diff --git a/verifier_test.sh b/verifier_test.sh index 6350e80d3..378b0e5ff 100755 --- a/verifier_test.sh +++ b/verifier_test.sh @@ -1,11 +1,7 @@ #!/bin/bash # -# A test suite for applypatch. Run in a client where you have done -# envsetup, choosecombo, etc. -# -# DO NOT RUN THIS ON A DEVICE YOU CARE ABOUT. It will mess up your -# system partition. -# +# A test suite for recovery's package signature verifier. Run in a +# client where you have done envsetup, lunch, etc. # # TODO: find some way to get this run regularly along with the rest of # the tests. @@ -77,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 From e672fdfefb0f2febfb0312257dd2fe2b09e2d81c Mon Sep 17 00:00:00 2001 From: jt1134 Date: Thu, 25 Oct 2012 20:48:46 -0500 Subject: [PATCH 005/104] move "install zip from sideload" to installation submenu * Also includes the below commit: fix installation from internal sdcard bug introduced in previous commit Change-Id: If41d409801e9a58df9515ac783bf503f80909d3e * I've done some minor changes to the original commit, which includes but is not limited to moving the "install zip from sideload" above "/sdcard/update.zip". Signed-off-by: Chirayu Desai Change-Id: I27a763a0d41085719a6c7d9c3b8a20fbcae4b072 --- default_recovery_ui.c | 3 +-- extendedcommands.c | 18 ++++++++++++------ recovery.c | 6 +----- recovery_ui.h | 14 +++++++------- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/default_recovery_ui.c b/default_recovery_ui.c index 1f801f193..032c1b304 100644 --- a/default_recovery_ui.c +++ b/default_recovery_ui.c @@ -23,8 +23,7 @@ char* MENU_HEADERS[] = { NULL }; char* MENU_ITEMS[] = { "reboot system now", - "install zip from sdcard", - "install zip from sideload", + "install zip", "wipe data/factory reset", "wipe cache partition", "backup and restore", diff --git a/extendedcommands.c b/extendedcommands.c index 3dd4d2a8d..5b6e52f23 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -41,6 +41,7 @@ #include "bmlutils/bmlutils.h" #include "cutils/android_reboot.h" +#include "adb_install.h" int signature_check_enabled = 1; int script_assert_enabled = 1; @@ -119,18 +120,20 @@ int install_zip(const char* packagefilepath) } #define ITEM_CHOOSE_ZIP 0 -#define ITEM_APPLY_UPDATE 1 -#define ITEM_SIG_CHECK 2 -#define ITEM_CHOOSE_ZIP_INT 3 +#define ITEM_APPLY_SIDELOAD 1 +#define ITEM_APPLY_UPDATE 2 // /sdcard/update.zip +#define ITEM_SIG_CHECK 3 +#define ITEM_CHOOSE_ZIP_INT 4 void show_install_update_menu() { - static char* headers[] = { "Apply update from .zip file on SD card", + static char* headers[] = { "Install update from zip file", "", NULL }; char* install_menu_items[] = { "choose zip from sdcard", + "install zip from sideload", "apply /sdcard/update.zip", "toggle signature verification", NULL, @@ -139,11 +142,11 @@ void show_install_update_menu() char *other_sd = NULL; if (volume_for_path("/emmc") != NULL) { other_sd = "/emmc/"; - install_menu_items[3] = "choose zip from internal sdcard"; + install_menu_items[4] = "choose zip from internal sdcard"; } else if (volume_for_path("/external_sd") != NULL) { other_sd = "/external_sd/"; - install_menu_items[3] = "choose zip from external sdcard"; + install_menu_items[4] = "choose zip from external sdcard"; } for (;;) @@ -164,6 +167,9 @@ void show_install_update_menu() show_choose_zip_menu("/sdcard/"); write_recovery_version(); break; + case ITEM_APPLY_SIDELOAD: + apply_from_adb(); + break; case ITEM_CHOOSE_ZIP_INT: if (other_sd != NULL) show_choose_zip_menu(other_sd); diff --git a/recovery.c b/recovery.c index 6778ac921..98b65ab0b 100644 --- a/recovery.c +++ b/recovery.c @@ -718,14 +718,10 @@ prompt_and_wait() { } break; - case ITEM_APPLY_SDCARD: + case ITEM_APPLY_ZIP: show_install_update_menu(); break; - case ITEM_APPLY_SIDELOAD: - apply_from_adb(); - break; - case ITEM_NANDROID: show_nandroid_menu(); break; diff --git a/recovery_ui.h b/recovery_ui.h index 101e6be0d..ec88d9586 100644 --- a/recovery_ui.h +++ b/recovery_ui.h @@ -75,15 +75,15 @@ int device_wipe_data(); #define ITEM_REBOOT 0 #define ITEM_APPLY_EXT 1 #define ITEM_APPLY_SDCARD 1 // historical synonym for ITEM_APPLY_EXT -#define ITEM_APPLY_SIDELOAD 2 -#define ITEM_WIPE_DATA 3 -#define ITEM_WIPE_CACHE 4 +#define ITEM_APPLY_ZIP 1 // used for installing an update from a zip +#define ITEM_WIPE_DATA 2 +#define ITEM_WIPE_CACHE 3 // unused in cwr #define ITEM_APPLY_CACHE 4 -#define ITEM_NANDROID 5 -#define ITEM_PARTITION 6 -#define ITEM_ADVANCED 7 -#define ITEM_POWEROFF 8 +#define ITEM_NANDROID 4 +#define ITEM_PARTITION 5 +#define ITEM_ADVANCED 6 +#define ITEM_POWEROFF 7 // Header text to display above the main menu. extern char* MENU_HEADERS[]; From 71d76c24dcdbdfaf4508c9121c794e4a324177cd Mon Sep 17 00:00:00 2001 From: Jin Feng Date: Tue, 4 Jun 2013 17:46:24 +0800 Subject: [PATCH 006/104] Fix the potential segmentation fault Extral newline can trigger recovery segmentation fault Test case: host$ adb shell 'echo -en "--update_package=ota_update.zip\n--show_text\n\n" > /cache/recovery/command' host$ adb reboot recovery Change-Id: If1781c1f5ad94a273f1cb122b67cedd9fb562433 Signed-off-by: Jin Feng --- recovery.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/recovery.c b/recovery.c index 98b65ab0b..8cfaa73a1 100644 --- a/recovery.c +++ b/recovery.c @@ -203,6 +203,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 @@ -210,7 +211,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); From 7ac751f6ea3e71651bfe54083a5b9a93647f0331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Sat, 8 Jun 2013 12:53:59 -0300 Subject: [PATCH 007/104] recovery: use just the mount point instead of the full path When resorting to using mount because a block device uses 'auto' filesystem, we should use the mount point and not the full path. This fixes an issue with nandroid to external sd on yuga. Change-Id: Idbff944ea6d5af9112644c2ed46e23ea0a33e5ee --- roots.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roots.c b/roots.c index a42e3552d..44fa14ae6 100644 --- a/roots.c +++ b/roots.c @@ -298,7 +298,7 @@ int ensure_path_mounted_at_mount_point(const char* path, const char* mount_point } else { // let's try mounting with the mount binary and hope for the best. char mount_cmd[PATH_MAX]; - sprintf(mount_cmd, "mount %s", path); + sprintf(mount_cmd, "mount %s", mount_point); return __system(mount_cmd); } From 593b54c8f46d40f31bc47504fba3a0b9c2cdc573 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Tue, 18 Jun 2013 20:10:19 -0700 Subject: [PATCH 008/104] fix stat usage Change-Id: Iab3290adca1602a23fc407d2b3f50b9aeac60389 --- nandroid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nandroid.c b/nandroid.c index 2f872d15c..f343c1d3d 100644 --- a/nandroid.c +++ b/nandroid.c @@ -597,7 +597,7 @@ int nandroid_restore_partition_extended(const char* backup_path, const char* mou restore_handler = tar_extract_wrapper; strcpy(tmp, "/proc/self/fd/0"); } - else if (0 != (ret = statfs(tmp, &file_info))) { + else if (0 != (ret = stat(tmp, &file_info))) { // can't find the backup, it may be the new backup format? // iterate through the backup types printf("couldn't find default\n"); From a1d58193b1f9973a4be0e646c69027681e69cf62 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Sat, 8 Jun 2013 08:53:29 -0700 Subject: [PATCH 009/104] Fix usage of stat vs statfs Change-Id: Id59b79f079c587630f5f6ed0418a45f2b8cd99ff Conflicts: nandroid.c --- nandroid.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/nandroid.c b/nandroid.c index f343c1d3d..32b93f8b2 100644 --- a/nandroid.c +++ b/nandroid.c @@ -356,12 +356,13 @@ int nandroid_backup(const char* backup_path) if (is_data_media_volume_path(volume->mount_point)) volume = volume_for_path("/data"); int ret; - struct statfs s; + struct statfs sfs; + struct stat s; if (NULL != volume) { - if (0 != (ret = statfs(volume->mount_point, &s))) + if (0 != (ret = statfs(volume->mount_point, &sfs))) return print_and_error("Unable to stat backup path.\n"); - uint64_t bavail = s.f_bavail; - uint64_t bsize = s.f_bsize; + uint64_t bavail = sfs.f_bavail; + uint64_t bsize = sfs.f_bsize; uint64_t sdcard_free = bavail * bsize; uint64_t sdcard_free_mb = sdcard_free / (uint64_t)(1024 * 1024); ui_print("SD Card space free: %lluMB\n", sdcard_free_mb); @@ -605,19 +606,19 @@ int nandroid_restore_partition_extended(const char* backup_path, const char* mou int i = 0; while ((filesystem = filesystems[i]) != NULL) { sprintf(tmp, "%s/%s.%s.img", backup_path, name, filesystem); - if (0 == (ret = statfs(tmp, &file_info))) { + if (0 == (ret = stat(tmp, &file_info))) { backup_filesystem = filesystem; restore_handler = unyaffs_wrapper; break; } sprintf(tmp, "%s/%s.%s.tar", backup_path, name, filesystem); - if (0 == (ret = statfs(tmp, &file_info))) { + if (0 == (ret = stat(tmp, &file_info))) { backup_filesystem = filesystem; restore_handler = tar_extract_wrapper; break; } sprintf(tmp, "%s/%s.%s.dup", backup_path, name, filesystem); - if (0 == (ret = statfs(tmp, &file_info))) { + if (0 == (ret = stat(tmp, &file_info))) { backup_filesystem = filesystem; restore_handler = dedupe_extract_wrapper; break; From f5724598115694a3ffa58ae1a4c48e8b2efecfbf Mon Sep 17 00:00:00 2001 From: Nathan Grebowiec Date: Mon, 1 Jul 2013 16:17:51 -0500 Subject: [PATCH 010/104] Bump to 6.0.3.3 Change-Id: I089fad25002a28cc5b368288c5bb34c37f9752bc --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 2cdf3ac07..2e0966f71 100644 --- a/Android.mk +++ b/Android.mk @@ -38,7 +38,7 @@ RECOVERY_NAME := CWM-based Recovery endif endif -RECOVERY_VERSION := $(RECOVERY_NAME) v6.0.3.2 +RECOVERY_VERSION := $(RECOVERY_NAME) v6.0.3.3 LOCAL_CFLAGS += -DRECOVERY_VERSION="$(RECOVERY_VERSION)" RECOVERY_API_VERSION := 2 From cfb115f449a1905ffc8bd2a2bdfa2a322ee56f80 Mon Sep 17 00:00:00 2001 From: Arne Coucheron Date: Fri, 17 May 2013 02:56:26 +0200 Subject: [PATCH 011/104] Update for zlib changes Depends on http://review.cyanogenmod.org/#/c/37912/ Change-Id: I7d541813f520c3a3187f6c193e47b201cd220957 --- utilities/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utilities/Android.mk b/utilities/Android.mk index 37866af0c..0aa001c95 100755 --- a/utilities/Android.mk +++ b/utilities/Android.mk @@ -47,7 +47,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_MODULE := libminizip LOCAL_CFLAGS := -Dmain=minizip_main -D__ANDROID__ -DIOAPI_NO_64 LOCAL_C_INCLUDES := external/zlib -LOCAL_SRC_FILES := ../../../external/zlib/contrib/minizip/minizip.c ../../../external/zlib/contrib/minizip/zip.c ../../../external/zlib/contrib/minizip/ioapi.c +LOCAL_SRC_FILES := ../../../external/zlib/src/contrib/minizip/minizip.c ../../../external/zlib/src/contrib/minizip/zip.c ../../../external/zlib/src/contrib/minizip/ioapi.c include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) From 64615ee6b4a9bb6cead931ced002bc604a696022 Mon Sep 17 00:00:00 2001 From: Chirayu Desai Date: Thu, 18 Jul 2013 21:54:22 +0530 Subject: [PATCH 012/104] SELinux: don't suppress the "no file_contexts" message on the UI Change-Id: I1456e7e2d2a1f3e143c084e1410dd31c505793ad --- recovery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recovery.c b/recovery.c index 8cfaa73a1..9d285a910 100644 --- a/recovery.c +++ b/recovery.c @@ -894,7 +894,7 @@ main(int argc, char **argv) { if (!sehandle) { fprintf(stderr, "Warning: No file_contexts\n"); - // ui_print("Warning: No file_contexts\n"); + ui_print("Warning: No file_contexts\n"); } LOGI("device_recovery_start()\n"); From ab60a0786caae012a071ec2f972b983385c55809 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Fri, 19 Jul 2013 16:19:11 -0700 Subject: [PATCH 013/104] add --headless mode Change-Id: I6338ed1d093a8db20250bd060cf97896db268976 --- common.h | 1 + recovery.c | 10 +++++++++- res/images/icon_cid.png | Bin 0 -> 36007 bytes ui.c | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 res/images/icon_cid.png diff --git a/common.h b/common.h index 7580f3222..bf81b48ad 100644 --- a/common.h +++ b/common.h @@ -65,6 +65,7 @@ enum { BACKGROUND_ICON_INSTALLING, BACKGROUND_ICON_ERROR, BACKGROUND_ICON_CLOCKWORK, + BACKGROUND_ICON_CID, BACKGROUND_ICON_FIRMWARE_INSTALLING, BACKGROUND_ICON_FIRMWARE_ERROR, NUM_BACKGROUND_ICONS diff --git a/recovery.c b/recovery.c index 8cfaa73a1..83ef59c09 100644 --- a/recovery.c +++ b/recovery.c @@ -53,6 +53,7 @@ struct selabel_handle *sehandle = NULL; static const struct option OPTIONS[] = { { "send_intent", required_argument, NULL, 's' }, { "update_package", required_argument, NULL, 'u' }, + { "headless", no_argument, NULL, 'h' }, { "wipe_data", no_argument, NULL, 'w' }, { "wipe_cache", no_argument, NULL, 'c' }, { "show_text", no_argument, NULL, 't' }, @@ -864,6 +865,7 @@ main(int argc, char **argv) { const char *update_package = NULL; int wipe_data = 0, wipe_cache = 0; int sideload = 0; + int headless = 0; LOGI("Checking arguments.\n"); int arg; @@ -877,6 +879,11 @@ main(int argc, char **argv) { wipe_data = wipe_cache = 1; #endif break; + case 'h': + ui_set_background(BACKGROUND_ICON_CID); + ui_show_text(0); + headless = 1; + break; case 'c': wipe_cache = 1; break; case 't': ui_show_text(1); break; case 'l': sideload = 1; break; @@ -941,7 +948,8 @@ main(int argc, char **argv) { if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n"); } else if (sideload) { signature_check_enabled = 0; - ui_set_show_text(1); + if (!headless) + ui_set_show_text(1); if (0 == apply_from_adb()) { status = INSTALL_SUCCESS; ui_set_show_text(0); diff --git a/res/images/icon_cid.png b/res/images/icon_cid.png new file mode 100644 index 0000000000000000000000000000000000000000..167ee38b9bfc9ae1e99690155354a8a826f1750f GIT binary patch literal 36007 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 literal 0 HcmV?d00001 diff --git a/ui.c b/ui.c index 6546b7919..20f5223ea 100644 --- a/ui.c +++ b/ui.c @@ -81,6 +81,7 @@ static const struct { gr_surface* surface; const char *name; } BITMAPS[] = { { &gBackgroundIcon[BACKGROUND_ICON_INSTALLING], "icon_installing" }, { &gBackgroundIcon[BACKGROUND_ICON_ERROR], "icon_error" }, { &gBackgroundIcon[BACKGROUND_ICON_CLOCKWORK], "icon_clockwork" }, + { &gBackgroundIcon[BACKGROUND_ICON_CID], "icon_cid" }, { &gBackgroundIcon[BACKGROUND_ICON_FIRMWARE_INSTALLING], "icon_firmware_install" }, { &gBackgroundIcon[BACKGROUND_ICON_FIRMWARE_ERROR], "icon_firmware_error" }, { &gProgressBarEmpty, "progress_empty" }, From a81817cbd8d0e7e5e845b6489edae1d87ac9a1d5 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Tue, 23 Jul 2013 11:55:16 -0700 Subject: [PATCH 014/104] more headless fixes Change-Id: I25243f67b8bcbef41867497b442d1b7f38f638dd --- Android.mk | 2 +- recovery.c | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Android.mk b/Android.mk index 2e0966f71..35562185d 100644 --- a/Android.mk +++ b/Android.mk @@ -38,7 +38,7 @@ RECOVERY_NAME := CWM-based Recovery endif endif -RECOVERY_VERSION := $(RECOVERY_NAME) v6.0.3.3 +RECOVERY_VERSION := $(RECOVERY_NAME) v6.0.3.4 LOCAL_CFLAGS += -DRECOVERY_VERSION="$(RECOVERY_VERSION)" RECOVERY_API_VERSION := 2 diff --git a/recovery.c b/recovery.c index 7281c15bc..6e8555ef2 100644 --- a/recovery.c +++ b/recovery.c @@ -680,6 +680,15 @@ wipe_data(int confirm) { ui_print("Data wipe complete.\n"); } +static void headless_wait() { + ui_show_text(0); + char** headers = prepend_title((const char**)MENU_HEADERS); + for (;;) { + finish_recovery(NULL); + get_menu_selection(headers, MENU_ITEMS, 0, 0); + } +} + int ui_menu_level = 1; int ui_root_menu = 0; static void @@ -962,8 +971,10 @@ main(int argc, char **argv) { signature_check_enabled = 0; script_assert_enabled = 0; is_user_initiated_recovery = 1; - ui_set_show_text(1); - ui_set_background(BACKGROUND_ICON_CLOCKWORK); + if (!headless) { + ui_set_show_text(1); + ui_set_background(BACKGROUND_ICON_CLOCKWORK); + } if (extendedcommand_file_exists()) { LOGI("Running extendedcommand...\n"); @@ -982,11 +993,14 @@ main(int argc, char **argv) { setup_adbd(); + if (headless) { + headless_wait(); + } if (status != INSTALL_SUCCESS && !is_user_initiated_recovery) { ui_set_show_text(1); ui_set_background(BACKGROUND_ICON_ERROR); } - if (status != INSTALL_SUCCESS || ui_text_visible()) { + else if (status != INSTALL_SUCCESS || ui_text_visible()) { prompt_and_wait(); } From 8736cc11d9dc7f4921008a4a5744092103cdac11 Mon Sep 17 00:00:00 2001 From: Michael Bestas Date: Tue, 23 Jul 2013 21:25:38 +0300 Subject: [PATCH 015/104] Remove "fix permissions" option Original discussion here: http://review.cyanogenmod.org/45559 Patchset 2: really remove the option Patchset 3: fix options that I broke on ps2 Signed-off-by: Michael Bestas Change-Id: I54877a24444c24a296c8c044efd977f30cc66de4 --- extendedcommands.c | 18 +- utilities/Android.mk | 8 - utilities/fix_permissions | 484 -------------------------------------- 3 files changed, 5 insertions(+), 505 deletions(-) delete mode 100755 utilities/fix_permissions diff --git a/extendedcommands.c b/extendedcommands.c index 5b6e52f23..6759335c5 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -1322,7 +1322,6 @@ void show_advanced_menu() "report error", "key test", "show log", - "fix permissions", "partition sdcard", "partition external sdcard", "partition internal sdcard", @@ -1330,13 +1329,13 @@ void show_advanced_menu() }; if (!can_partition("/sdcard")) { - list[6] = NULL; + list[5] = NULL; } if (!can_partition("/external_sd")) { - list[7] = NULL; + list[6] = NULL; } if (!can_partition("/emmc")) { - list[8] = NULL; + list[7] = NULL; } for (;;) @@ -1384,19 +1383,12 @@ void show_advanced_menu() ui_printlogtail(12); break; case 5: - ensure_path_mounted("/system"); - ensure_path_mounted("/data"); - ui_print("Fixing permissions...\n"); - __system("fix_permissions"); - ui_print("Done!\n"); - break; - case 6: partition_sdcard("/sdcard"); break; - case 7: + case 6: partition_sdcard("/external_sd"); break; - case 8: + case 7: partition_sdcard("/emmc"); break; } diff --git a/utilities/Android.mk b/utilities/Android.mk index 0aa001c95..49174fee8 100755 --- a/utilities/Android.mk +++ b/utilities/Android.mk @@ -1,13 +1,5 @@ LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) -LOCAL_MODULE := fix_permissions -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin -LOCAL_SRC_FILES := $(LOCAL_MODULE) -include $(BUILD_PREBUILT) - include $(CLEAR_VARS) LOCAL_MODULE := parted LOCAL_MODULE_TAGS := optional diff --git a/utilities/fix_permissions b/utilities/fix_permissions deleted file mode 100755 index a6db51469..000000000 --- a/utilities/fix_permissions +++ /dev/null @@ -1,484 +0,0 @@ -#! /system/bin/sh -# -# Warning: if you want to run this script in cm-recovery change the above to #!/sbin/sh -# -# fix_permissions - fixes permissions on Android data directories after upgrade -# shade@chemlab.org -# -# original concept: http://blog.elsdoerfer.name/2009/05/25/android-fix-package-uid-mismatches/ -# implementation by: Cyanogen -# improved by: ankn, smeat, thenefield, farmatito, rikupw, Kastro -# -# v1.1-v1.31r3 - many improvements and concepts from XDA developers. -# v1.34 through v2.00 - A lot of frustration [by Kastro] -# v2.01 - Completely rewrote the script for SPEED, thanks for the input farmatito -# /data/data depth recursion is tweaked; -# fixed single mode; -# functions created for modularity; -# logging can be disabled via CLI for more speed; -# runtime computation added to end (Runtime: mins secs); -# progress (current # of total) added to screen; -# fixed CLI argument parsing, now you can have more than one option!; -# debug cli option; -# verbosity can be disabled via CLI option for less noise;; -# [by Kastro, (XDA: k4str0), twitter;mattcarver] -# v2.02 - ignore com.htc.resources.apk if it exists and minor code cleanups, -# fix help text, implement simulated run (-s) [farmatito] -# v2.03 - fixed chown group ownership output [Kastro] -# v2.04 - replaced /system/sd with $SD_EXT_DIRECTORY [Firerat] -VERSION="2.04" - -# Defaults -DEBUG=0 # Debug off by default -LOGGING=1 # Logging on by default -VERBOSE=1 # Verbose on by default - -# Messages -UID_MSG="Changing user ownership for:" -GID_MSG="Changing group ownership for:" -PERM_MSG="Changing permissions for:" - -# Programs needed -ECHO="busybox echo" -GREP="busybox grep" -EGREP="busybox egrep" -CAT="busybox cat" -CHOWN="busybox chown" -CHMOD="busybox chmod" -MOUNT="busybox mount" -UMOUNT="busybox umount" -CUT="busybox cut" -FIND="busybox find" -LS="busybox ls" -TR="busybox tr" -TEE="busybox tee" -TEST="busybox test" -SED="busybox sed" -RM="busybox rm" -WC="busybox wc" -EXPR="busybox expr" -DATE="busybox date" - -# Initialise vars -CODEPATH="" -UID="" -GID="" -PACKAGE="" -REMOVE=0 -NOSYSTEM=0 -ONLY_ONE="" -SIMULATE=0 -SYSREMOUNT=0 -SYSMOUNT=0 -DATAMOUNT=0 -SYSSDMOUNT=0 -FP_STARTTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" ) -FP_STARTEPOCH=$( $DATE +%s ) -if $TEST "$SD_EXT_DIRECTORY" = ""; then - #check for mount point, /system/sd included in tests for backward compatibility - for MP in /sd-ext /system/sd;do - if $TEST -d $MP; then - SD_EXT_DIRECTORY=$MP - break - fi - done -fi -fp_usage() -{ - $ECHO "Usage $0 [OPTIONS] [APK_PATH]" - $ECHO " -d turn on debug" - $ECHO " -f fix only package APK_PATH" - $ECHO " -l disable logging for this run (faster)" - $ECHO " -r remove stale data directories" - $ECHO " of uninstalled packages while fixing permissions" - $ECHO " -s simulate only" - $ECHO " -u check only non-system directories" - $ECHO " -v disable verbosity for this run (less output)" - $ECHO " -V print version" - $ECHO " -h this help" -} - -fp_parseargs() -{ - # Parse options - while $TEST $# -ne 0; do - case "$1" in - -d) - DEBUG=1 - ;; - -f) - if $TEST $# -lt 2; then - $ECHO "$0: missing argument for option $1" - exit 1 - else - if $TEST $( $ECHO $2 | $CUT -c1 ) != "-"; then - ONLY_ONE=$2 - shift; - else - $ECHO "$0: missing argument for option $1" - exit 1 - fi - fi - ;; - -r) - REMOVE=1 - ;; - -s) - SIMULATE=1 - ;; - -l) - if $TEST $LOGGING -eq 0; then - LOGGING=1 - else - LOGGING=0 - fi - ;; - -v) - if $TEST $VERBOSE -eq 0; then - VERBOSE=1 - else - VERBOSE=0 - fi - ;; - -u) - NOSYSTEM=1 - ;; - -V) - $ECHO "$0 $VERSION" - exit 0 - ;; - -h) - fp_usage - exit 0 - ;; - -*) - $ECHO "$0: unknown option $1" - $ECHO - fp_usage - exit 1 - ;; - esac - shift; - done -} - -fp_print() -{ - MSG=$@ - if $TEST $LOGGING -eq 1; then - $ECHO $MSG | $TEE -a $LOG_FILE - else - $ECHO $MSG - fi -} - -fp_start() -{ - if $TEST $SIMULATE -eq 0 ; then - if $TEST $( $GREP -c " /system " "/proc/mounts" ) -ne 0; then - DEVICE=$( $GREP " /system " "/proc/mounts" | $CUT -d ' ' -f1 ) - if $TEST $DEBUG -eq 1; then - fp_print "/system mounted on $DEVICE" - fi - if $TEST $( $GREP " /system " "/proc/mounts" | $GREP -c " ro " ) -ne 0; then - $MOUNT -o remount,rw $DEVICE /system - SYSREMOUNT=1 - fi - else - $MOUNT /system > /dev/null 2>&1 - SYSMOUNT=1 - fi - - if $TEST $( $GREP -c " /data " "/proc/mounts" ) -eq 0; then - $MOUNT /data > /dev/null 2>&1 - DATAMOUNT=1 - fi - - if $TEST -e /dev/block/mmcblk0p2 && $TEST $( $GREP -c " $SD_EXT_DIRECTORY " "/proc/mounts" ) -eq 0; then - $MOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1 - SYSSDMOUNT=1 - fi - fi - if $TEST $( $MOUNT | $GREP -c /sdcard ) -eq 0; then - LOG_FILE="/data/fix_permissions.log" - else - LOG_FILE="/sdcard/fix_permissions.log" - fi - if $TEST ! -e "$LOG_FILE"; then - > $LOG_FILE - fi - - fp_print "$0 $VERSION started at $FP_STARTTIME" -} - -fp_chown_uid() -{ - FP_OLDUID=$1 - FP_UID=$2 - FP_FILE=$3 - - #if user ownership doesn't equal then change them - if $TEST "$FP_OLDUID" != "$FP_UID"; then - if $TEST $VERBOSE -ne 0; then - fp_print "$UID_MSG $FP_FILE from '$FP_OLDUID' to '$FP_UID'" - fi - if $TEST $SIMULATE -eq 0; then - $CHOWN $FP_UID "$FP_FILE" - fi - fi -} - -fp_chown_gid() -{ - FP_OLDGID=$1 - FP_GID=$2 - FP_FILE=$3 - - #if group ownership doesn't equal then change them - if $TEST "$FP_OLDGID" != "$FP_GID"; then - if $TEST $VERBOSE -ne 0; then - fp_print "$GID_MSG $FP_FILE from '$FP_OLDGID' to '$FP_GID'" - fi - if $TEST $SIMULATE -eq 0; then - $CHOWN :$FP_GID "$FP_FILE" - fi - fi -} - -fp_chmod() -{ - FP_OLDPER=$1 - FP_OLDPER=$( $ECHO $FP_OLDPER | cut -c2-10 ) - FP_PERSTR=$2 - FP_PERNUM=$3 - FP_FILE=$4 - - #if the permissions are not equal - if $TEST "$FP_OLDPER" != "$FP_PERSTR"; then - if $TEST $VERBOSE -ne 0; then - fp_print "$PERM_MSG $FP_FILE from '$FP_OLDPER' to '$FP_PERSTR' ($FP_PERNUM)" - fi - #change the permissions - if $TEST $SIMULATE -eq 0; then - $CHMOD $FP_PERNUM "$FP_FILE" - fi - fi -} - -fp_all() -{ - FP_NUMS=$( $CAT /data/system/packages.xml | $EGREP "^ /dev/null 2>&1 - fi - - if $TEST $SYSSDMOUNT -eq 1; then - $UMOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1 - fi - - if $TEST $SYSMOUNT -eq 1; then - $UMOUNT /system > /dev/null 2>&1 - fi - - if $TEST $DATAMOUNT -eq 1; then - $UMOUNT /data > /dev/null 2>&1 - fi - - FP_ENDTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" ) - FP_ENDEPOCH=$( $DATE +%s ) - - date_diff $FP_STARTEPOCH $FP_ENDEPOCH - - fp_print "$0 $VERSION ended at $FP_ENDTIME (Runtime:${FP_DDM}m${FP_DDS}s)" -} - -#MAIN SCRIPT - -fp_parseargs $@ -fp_start -if $TEST "$ONLY_ONE" != "" -a "$ONLY_ONE" != "0" ; then - fp_single "$ONLY_ONE" -else - fp_all -fi -fp_end From b9ecf6480514bdd21e5a546a7ff376920b31153b Mon Sep 17 00:00:00 2001 From: Michael Bestas Date: Fri, 12 Jul 2013 13:29:45 +0300 Subject: [PATCH 016/104] Add power off & reboot to bootloader mode in advanced menu Also: Cleanup leftovers from previous poweroff option Always ensure a clean reboot (Thanks to PhilZ for his implementation) Bootloader option defaults to "bootloader" (fastboot mode), it can be overriden by defining "ro.bootloader.mode=download" prop for samsung download mode We leave reboot to bootloader option always enabled, that way we can override the get_filtered_menu_selection bug that breaks the order of the options when NULLifying menu options. Signed-off-by: Michael Bestas Change-Id: I6d674b37ab41ab342b105993719277360b4707e4 --- extendedcommands.c | 48 ++++++++++++++++++++++++++++++++++++---------- recovery.c | 13 ++++++++----- recovery_ui.h | 4 +++- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/extendedcommands.c b/extendedcommands.c index 6759335c5..fb64e759c 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -1318,6 +1318,8 @@ void show_advanced_menu() }; static char* list[] = { "reboot recovery", + "reboot to bootloader", + "power off", "wipe dalvik cache", "report error", "key test", @@ -1328,14 +1330,20 @@ void show_advanced_menu() NULL }; + char bootloader_mode[PROPERTY_VALUE_MAX]; + property_get("ro.bootloader.mode", bootloader_mode, ""); + if (!strcmp(bootloader_mode, "download")) { + list[1] = "reboot to download mode"; + } + if (!can_partition("/sdcard")) { - list[5] = NULL; + list[7] = NULL; } if (!can_partition("/external_sd")) { - list[6] = NULL; + list[8] = NULL; } if (!can_partition("/emmc")) { - list[7] = NULL; + list[9] = NULL; } for (;;) @@ -1346,9 +1354,29 @@ void show_advanced_menu() switch (chosen_item) { case 0: - android_reboot(ANDROID_RB_RESTART2, 0, "recovery"); + { + ui_print("Rebooting recovery...\n"); + reboot_main_system(ANDROID_RB_RESTART2, 0, "recovery"); break; + } case 1: + { + if (!strcmp(bootloader_mode, "download")) { + ui_print("Rebooting to download mode...\n"); + reboot_main_system(ANDROID_RB_RESTART2, 0, "download"); + } else { + ui_print("Rebooting to bootloader...\n"); + reboot_main_system(ANDROID_RB_RESTART2, 0, "bootloader"); + } + break; + } + case 2: + { + ui_print("Shutting down...\n"); + reboot_main_system(ANDROID_RB_POWEROFF, 0, 0); + break; + } + case 3: if (0 != ensure_path_mounted("/data")) break; ensure_path_mounted("/sd-ext"); @@ -1361,10 +1389,10 @@ void show_advanced_menu() } ensure_path_unmounted("/data"); break; - case 2: + case 4: handle_failure(1); break; - case 3: + case 5: { ui_print("Outputting key codes.\n"); ui_print("Go back to end debugging.\n"); @@ -1379,16 +1407,16 @@ void show_advanced_menu() while (action != GO_BACK); break; } - case 4: + case 6: ui_printlogtail(12); break; - case 5: + case 7: partition_sdcard("/sdcard"); break; - case 6: + case 8: partition_sdcard("/external_sd"); break; - case 7: + case 9: partition_sdcard("/emmc"); break; } diff --git a/recovery.c b/recovery.c index 6e8555ef2..9e54147b1 100644 --- a/recovery.c +++ b/recovery.c @@ -716,7 +716,7 @@ prompt_and_wait() { int status; switch (chosen_item) { case ITEM_REBOOT: - poweroff=0; + poweroff = 0; return; case ITEM_WIPE_DATA: @@ -749,10 +749,6 @@ prompt_and_wait() { case ITEM_ADVANCED: show_advanced_menu(); break; - - case ITEM_POWEROFF: - poweroff = 1; - return; } } } @@ -795,6 +791,13 @@ setup_adbd() { property_set("service.adb.root", "1"); } +// call a clean reboot +void reboot_main_system(int cmd, int flags, char *arg) { + verify_root_and_recovery(); + finish_recovery(NULL); // sync() in here + android_reboot(cmd, flags, arg); +} + int main(int argc, char **argv) { diff --git a/recovery_ui.h b/recovery_ui.h index ec88d9586..0555122bc 100644 --- a/recovery_ui.h +++ b/recovery_ui.h @@ -83,7 +83,6 @@ int device_wipe_data(); #define ITEM_NANDROID 4 #define ITEM_PARTITION 5 #define ITEM_ADVANCED 6 -#define ITEM_POWEROFF 7 // Header text to display above the main menu. extern char* MENU_HEADERS[]; @@ -102,4 +101,7 @@ set_sdcard_update_bootloader_message(); extern int ui_handle_key(int key, int visible); +// call a clean reboot +void reboot_main_system(int cmd, int flags, char *arg); + #endif From f427a7ddcdad76efc2c82298f23a97139d77567e Mon Sep 17 00:00:00 2001 From: Ricardo Cerqueira Date: Fri, 26 Jul 2013 00:21:26 +0100 Subject: [PATCH 017/104] cwm: Add liblog dependency Change-Id: Ic5716e5f2c311406bc6384e63fb0f217113e6eeb --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 35562185d..aefe03dd5 100644 --- a/Android.mk +++ b/Android.mk @@ -97,7 +97,7 @@ ifeq ($(BOARD_USES_BML_OVER_MTD),true) LOCAL_STATIC_LIBRARIES += libbml_over_mtd endif -LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils +LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils liblog LOCAL_STATIC_LIBRARIES += libstdc++ libc LOCAL_STATIC_LIBRARIES += libselinux From 62c6902b090fddf1f6040ccac3cd86b93300e6a7 Mon Sep 17 00:00:00 2001 From: Ricardo Cerqueira Date: Fri, 26 Jul 2013 01:47:41 +0100 Subject: [PATCH 018/104] Add a recovery version variable the releasetools want this now Change-Id: Ibc3cdbc8f5f8022bf51479a4702d1d0d043f70a9 --- Android.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Android.mk b/Android.mk index aefe03dd5..babf1f65f 100644 --- a/Android.mk +++ b/Android.mk @@ -29,6 +29,8 @@ LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true +RECOVERY_FSTAB_VERSION := 2 + ifdef I_AM_KOUSH RECOVERY_NAME := ClockworkMod Recovery LOCAL_CFLAGS += -DI_AM_KOUSH From 1a6c91fcbc16f90b3dcbc9cef3e1052c84a40ffb Mon Sep 17 00:00:00 2001 From: Ricardo Cerqueira Date: Fri, 26 Jul 2013 12:39:28 +0100 Subject: [PATCH 019/104] Use fstab v1 as a default. v1 is the old recovery.fstab format that everybody is still using. Devices using v2 should set RECOVERY_FSTAB_VERSION=2 in their board files Change-Id: Ie7221f110ecc594f6f92973d2c27da10f62b6431 --- Android.mk | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index babf1f65f..eac2eaf71 100644 --- a/Android.mk +++ b/Android.mk @@ -29,7 +29,9 @@ LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true -RECOVERY_FSTAB_VERSION := 2 +ifeq ($(RECOVERY_FSTAB_VERSION),) +RECOVERY_FSTAB_VERSION := 1 +endif ifdef I_AM_KOUSH RECOVERY_NAME := ClockworkMod Recovery From 96d9f223658fa32a7f354d965b82292f3fc013b2 Mon Sep 17 00:00:00 2001 From: Michael Bestas Date: Tue, 23 Jul 2013 21:25:38 +0300 Subject: [PATCH 020/104] Remove "fix permissions" option Original discussion here: http://review.cyanogenmod.org/45559 Patchset 2: really remove the option Patchset 3: fix options that I broke on ps2 Signed-off-by: Michael Bestas Change-Id: I54877a24444c24a296c8c044efd977f30cc66de4 --- extendedcommands.c | 18 +- utilities/Android.mk | 8 - utilities/fix_permissions | 484 -------------------------------------- 3 files changed, 5 insertions(+), 505 deletions(-) delete mode 100755 utilities/fix_permissions diff --git a/extendedcommands.c b/extendedcommands.c index 5b6e52f23..6759335c5 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -1322,7 +1322,6 @@ void show_advanced_menu() "report error", "key test", "show log", - "fix permissions", "partition sdcard", "partition external sdcard", "partition internal sdcard", @@ -1330,13 +1329,13 @@ void show_advanced_menu() }; if (!can_partition("/sdcard")) { - list[6] = NULL; + list[5] = NULL; } if (!can_partition("/external_sd")) { - list[7] = NULL; + list[6] = NULL; } if (!can_partition("/emmc")) { - list[8] = NULL; + list[7] = NULL; } for (;;) @@ -1384,19 +1383,12 @@ void show_advanced_menu() ui_printlogtail(12); break; case 5: - ensure_path_mounted("/system"); - ensure_path_mounted("/data"); - ui_print("Fixing permissions...\n"); - __system("fix_permissions"); - ui_print("Done!\n"); - break; - case 6: partition_sdcard("/sdcard"); break; - case 7: + case 6: partition_sdcard("/external_sd"); break; - case 8: + case 7: partition_sdcard("/emmc"); break; } diff --git a/utilities/Android.mk b/utilities/Android.mk index 0aa001c95..49174fee8 100755 --- a/utilities/Android.mk +++ b/utilities/Android.mk @@ -1,13 +1,5 @@ LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) -LOCAL_MODULE := fix_permissions -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES -LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin -LOCAL_SRC_FILES := $(LOCAL_MODULE) -include $(BUILD_PREBUILT) - include $(CLEAR_VARS) LOCAL_MODULE := parted LOCAL_MODULE_TAGS := optional diff --git a/utilities/fix_permissions b/utilities/fix_permissions deleted file mode 100755 index a6db51469..000000000 --- a/utilities/fix_permissions +++ /dev/null @@ -1,484 +0,0 @@ -#! /system/bin/sh -# -# Warning: if you want to run this script in cm-recovery change the above to #!/sbin/sh -# -# fix_permissions - fixes permissions on Android data directories after upgrade -# shade@chemlab.org -# -# original concept: http://blog.elsdoerfer.name/2009/05/25/android-fix-package-uid-mismatches/ -# implementation by: Cyanogen -# improved by: ankn, smeat, thenefield, farmatito, rikupw, Kastro -# -# v1.1-v1.31r3 - many improvements and concepts from XDA developers. -# v1.34 through v2.00 - A lot of frustration [by Kastro] -# v2.01 - Completely rewrote the script for SPEED, thanks for the input farmatito -# /data/data depth recursion is tweaked; -# fixed single mode; -# functions created for modularity; -# logging can be disabled via CLI for more speed; -# runtime computation added to end (Runtime: mins secs); -# progress (current # of total) added to screen; -# fixed CLI argument parsing, now you can have more than one option!; -# debug cli option; -# verbosity can be disabled via CLI option for less noise;; -# [by Kastro, (XDA: k4str0), twitter;mattcarver] -# v2.02 - ignore com.htc.resources.apk if it exists and minor code cleanups, -# fix help text, implement simulated run (-s) [farmatito] -# v2.03 - fixed chown group ownership output [Kastro] -# v2.04 - replaced /system/sd with $SD_EXT_DIRECTORY [Firerat] -VERSION="2.04" - -# Defaults -DEBUG=0 # Debug off by default -LOGGING=1 # Logging on by default -VERBOSE=1 # Verbose on by default - -# Messages -UID_MSG="Changing user ownership for:" -GID_MSG="Changing group ownership for:" -PERM_MSG="Changing permissions for:" - -# Programs needed -ECHO="busybox echo" -GREP="busybox grep" -EGREP="busybox egrep" -CAT="busybox cat" -CHOWN="busybox chown" -CHMOD="busybox chmod" -MOUNT="busybox mount" -UMOUNT="busybox umount" -CUT="busybox cut" -FIND="busybox find" -LS="busybox ls" -TR="busybox tr" -TEE="busybox tee" -TEST="busybox test" -SED="busybox sed" -RM="busybox rm" -WC="busybox wc" -EXPR="busybox expr" -DATE="busybox date" - -# Initialise vars -CODEPATH="" -UID="" -GID="" -PACKAGE="" -REMOVE=0 -NOSYSTEM=0 -ONLY_ONE="" -SIMULATE=0 -SYSREMOUNT=0 -SYSMOUNT=0 -DATAMOUNT=0 -SYSSDMOUNT=0 -FP_STARTTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" ) -FP_STARTEPOCH=$( $DATE +%s ) -if $TEST "$SD_EXT_DIRECTORY" = ""; then - #check for mount point, /system/sd included in tests for backward compatibility - for MP in /sd-ext /system/sd;do - if $TEST -d $MP; then - SD_EXT_DIRECTORY=$MP - break - fi - done -fi -fp_usage() -{ - $ECHO "Usage $0 [OPTIONS] [APK_PATH]" - $ECHO " -d turn on debug" - $ECHO " -f fix only package APK_PATH" - $ECHO " -l disable logging for this run (faster)" - $ECHO " -r remove stale data directories" - $ECHO " of uninstalled packages while fixing permissions" - $ECHO " -s simulate only" - $ECHO " -u check only non-system directories" - $ECHO " -v disable verbosity for this run (less output)" - $ECHO " -V print version" - $ECHO " -h this help" -} - -fp_parseargs() -{ - # Parse options - while $TEST $# -ne 0; do - case "$1" in - -d) - DEBUG=1 - ;; - -f) - if $TEST $# -lt 2; then - $ECHO "$0: missing argument for option $1" - exit 1 - else - if $TEST $( $ECHO $2 | $CUT -c1 ) != "-"; then - ONLY_ONE=$2 - shift; - else - $ECHO "$0: missing argument for option $1" - exit 1 - fi - fi - ;; - -r) - REMOVE=1 - ;; - -s) - SIMULATE=1 - ;; - -l) - if $TEST $LOGGING -eq 0; then - LOGGING=1 - else - LOGGING=0 - fi - ;; - -v) - if $TEST $VERBOSE -eq 0; then - VERBOSE=1 - else - VERBOSE=0 - fi - ;; - -u) - NOSYSTEM=1 - ;; - -V) - $ECHO "$0 $VERSION" - exit 0 - ;; - -h) - fp_usage - exit 0 - ;; - -*) - $ECHO "$0: unknown option $1" - $ECHO - fp_usage - exit 1 - ;; - esac - shift; - done -} - -fp_print() -{ - MSG=$@ - if $TEST $LOGGING -eq 1; then - $ECHO $MSG | $TEE -a $LOG_FILE - else - $ECHO $MSG - fi -} - -fp_start() -{ - if $TEST $SIMULATE -eq 0 ; then - if $TEST $( $GREP -c " /system " "/proc/mounts" ) -ne 0; then - DEVICE=$( $GREP " /system " "/proc/mounts" | $CUT -d ' ' -f1 ) - if $TEST $DEBUG -eq 1; then - fp_print "/system mounted on $DEVICE" - fi - if $TEST $( $GREP " /system " "/proc/mounts" | $GREP -c " ro " ) -ne 0; then - $MOUNT -o remount,rw $DEVICE /system - SYSREMOUNT=1 - fi - else - $MOUNT /system > /dev/null 2>&1 - SYSMOUNT=1 - fi - - if $TEST $( $GREP -c " /data " "/proc/mounts" ) -eq 0; then - $MOUNT /data > /dev/null 2>&1 - DATAMOUNT=1 - fi - - if $TEST -e /dev/block/mmcblk0p2 && $TEST $( $GREP -c " $SD_EXT_DIRECTORY " "/proc/mounts" ) -eq 0; then - $MOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1 - SYSSDMOUNT=1 - fi - fi - if $TEST $( $MOUNT | $GREP -c /sdcard ) -eq 0; then - LOG_FILE="/data/fix_permissions.log" - else - LOG_FILE="/sdcard/fix_permissions.log" - fi - if $TEST ! -e "$LOG_FILE"; then - > $LOG_FILE - fi - - fp_print "$0 $VERSION started at $FP_STARTTIME" -} - -fp_chown_uid() -{ - FP_OLDUID=$1 - FP_UID=$2 - FP_FILE=$3 - - #if user ownership doesn't equal then change them - if $TEST "$FP_OLDUID" != "$FP_UID"; then - if $TEST $VERBOSE -ne 0; then - fp_print "$UID_MSG $FP_FILE from '$FP_OLDUID' to '$FP_UID'" - fi - if $TEST $SIMULATE -eq 0; then - $CHOWN $FP_UID "$FP_FILE" - fi - fi -} - -fp_chown_gid() -{ - FP_OLDGID=$1 - FP_GID=$2 - FP_FILE=$3 - - #if group ownership doesn't equal then change them - if $TEST "$FP_OLDGID" != "$FP_GID"; then - if $TEST $VERBOSE -ne 0; then - fp_print "$GID_MSG $FP_FILE from '$FP_OLDGID' to '$FP_GID'" - fi - if $TEST $SIMULATE -eq 0; then - $CHOWN :$FP_GID "$FP_FILE" - fi - fi -} - -fp_chmod() -{ - FP_OLDPER=$1 - FP_OLDPER=$( $ECHO $FP_OLDPER | cut -c2-10 ) - FP_PERSTR=$2 - FP_PERNUM=$3 - FP_FILE=$4 - - #if the permissions are not equal - if $TEST "$FP_OLDPER" != "$FP_PERSTR"; then - if $TEST $VERBOSE -ne 0; then - fp_print "$PERM_MSG $FP_FILE from '$FP_OLDPER' to '$FP_PERSTR' ($FP_PERNUM)" - fi - #change the permissions - if $TEST $SIMULATE -eq 0; then - $CHMOD $FP_PERNUM "$FP_FILE" - fi - fi -} - -fp_all() -{ - FP_NUMS=$( $CAT /data/system/packages.xml | $EGREP "^ /dev/null 2>&1 - fi - - if $TEST $SYSSDMOUNT -eq 1; then - $UMOUNT $SD_EXT_DIRECTORY > /dev/null 2>&1 - fi - - if $TEST $SYSMOUNT -eq 1; then - $UMOUNT /system > /dev/null 2>&1 - fi - - if $TEST $DATAMOUNT -eq 1; then - $UMOUNT /data > /dev/null 2>&1 - fi - - FP_ENDTIME=$( $DATE +"%m-%d-%Y %H:%M:%S" ) - FP_ENDEPOCH=$( $DATE +%s ) - - date_diff $FP_STARTEPOCH $FP_ENDEPOCH - - fp_print "$0 $VERSION ended at $FP_ENDTIME (Runtime:${FP_DDM}m${FP_DDS}s)" -} - -#MAIN SCRIPT - -fp_parseargs $@ -fp_start -if $TEST "$ONLY_ONE" != "" -a "$ONLY_ONE" != "0" ; then - fp_single "$ONLY_ONE" -else - fp_all -fi -fp_end From 05ad6a15efe22a840e63ef9fa2f7b3d369220576 Mon Sep 17 00:00:00 2001 From: Michael Bestas Date: Fri, 12 Jul 2013 13:29:45 +0300 Subject: [PATCH 021/104] Add power off & reboot to bootloader mode in advanced menu Also: Cleanup leftovers from previous poweroff option Always ensure a clean reboot (Thanks to PhilZ for his implementation) Bootloader option defaults to "bootloader" (fastboot mode), it can be overriden by defining "ro.bootloader.mode=download" prop for samsung download mode We leave reboot to bootloader option always enabled, that way we can override the get_filtered_menu_selection bug that breaks the order of the options when NULLifying menu options. Signed-off-by: Michael Bestas Change-Id: I6d674b37ab41ab342b105993719277360b4707e4 --- extendedcommands.c | 48 ++++++++++++++++++++++++++++++++++++---------- recovery.c | 13 ++++++++----- recovery_ui.h | 4 +++- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/extendedcommands.c b/extendedcommands.c index 6759335c5..fb64e759c 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -1318,6 +1318,8 @@ void show_advanced_menu() }; static char* list[] = { "reboot recovery", + "reboot to bootloader", + "power off", "wipe dalvik cache", "report error", "key test", @@ -1328,14 +1330,20 @@ void show_advanced_menu() NULL }; + char bootloader_mode[PROPERTY_VALUE_MAX]; + property_get("ro.bootloader.mode", bootloader_mode, ""); + if (!strcmp(bootloader_mode, "download")) { + list[1] = "reboot to download mode"; + } + if (!can_partition("/sdcard")) { - list[5] = NULL; + list[7] = NULL; } if (!can_partition("/external_sd")) { - list[6] = NULL; + list[8] = NULL; } if (!can_partition("/emmc")) { - list[7] = NULL; + list[9] = NULL; } for (;;) @@ -1346,9 +1354,29 @@ void show_advanced_menu() switch (chosen_item) { case 0: - android_reboot(ANDROID_RB_RESTART2, 0, "recovery"); + { + ui_print("Rebooting recovery...\n"); + reboot_main_system(ANDROID_RB_RESTART2, 0, "recovery"); break; + } case 1: + { + if (!strcmp(bootloader_mode, "download")) { + ui_print("Rebooting to download mode...\n"); + reboot_main_system(ANDROID_RB_RESTART2, 0, "download"); + } else { + ui_print("Rebooting to bootloader...\n"); + reboot_main_system(ANDROID_RB_RESTART2, 0, "bootloader"); + } + break; + } + case 2: + { + ui_print("Shutting down...\n"); + reboot_main_system(ANDROID_RB_POWEROFF, 0, 0); + break; + } + case 3: if (0 != ensure_path_mounted("/data")) break; ensure_path_mounted("/sd-ext"); @@ -1361,10 +1389,10 @@ void show_advanced_menu() } ensure_path_unmounted("/data"); break; - case 2: + case 4: handle_failure(1); break; - case 3: + case 5: { ui_print("Outputting key codes.\n"); ui_print("Go back to end debugging.\n"); @@ -1379,16 +1407,16 @@ void show_advanced_menu() while (action != GO_BACK); break; } - case 4: + case 6: ui_printlogtail(12); break; - case 5: + case 7: partition_sdcard("/sdcard"); break; - case 6: + case 8: partition_sdcard("/external_sd"); break; - case 7: + case 9: partition_sdcard("/emmc"); break; } diff --git a/recovery.c b/recovery.c index 6e8555ef2..9e54147b1 100644 --- a/recovery.c +++ b/recovery.c @@ -716,7 +716,7 @@ prompt_and_wait() { int status; switch (chosen_item) { case ITEM_REBOOT: - poweroff=0; + poweroff = 0; return; case ITEM_WIPE_DATA: @@ -749,10 +749,6 @@ prompt_and_wait() { case ITEM_ADVANCED: show_advanced_menu(); break; - - case ITEM_POWEROFF: - poweroff = 1; - return; } } } @@ -795,6 +791,13 @@ setup_adbd() { property_set("service.adb.root", "1"); } +// call a clean reboot +void reboot_main_system(int cmd, int flags, char *arg) { + verify_root_and_recovery(); + finish_recovery(NULL); // sync() in here + android_reboot(cmd, flags, arg); +} + int main(int argc, char **argv) { diff --git a/recovery_ui.h b/recovery_ui.h index ec88d9586..0555122bc 100644 --- a/recovery_ui.h +++ b/recovery_ui.h @@ -83,7 +83,6 @@ int device_wipe_data(); #define ITEM_NANDROID 4 #define ITEM_PARTITION 5 #define ITEM_ADVANCED 6 -#define ITEM_POWEROFF 7 // Header text to display above the main menu. extern char* MENU_HEADERS[]; @@ -102,4 +101,7 @@ set_sdcard_update_bootloader_message(); extern int ui_handle_key(int key, int visible); +// call a clean reboot +void reboot_main_system(int cmd, int flags, char *arg); + #endif From d56d0fdfd532d630a239e907bf6dd073a1ddcabf Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Sat, 27 Jul 2013 21:52:05 -0700 Subject: [PATCH 022/104] Fixes for Superuser Change-Id: I86a280088ee8767e02ba51b72f4d011cf4658f6f --- su/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/su/Android.mk b/su/Android.mk index 6458e8e5d..8a11752d8 100644 --- a/su/Android.mk +++ b/su/Android.mk @@ -8,7 +8,7 @@ LOCAL_MODULE_TAGS := eng debug LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_STATIC_LIBRARIES := libc LOCAL_C_INCLUDES := external/sqlite/dist -LOCAL_SRC_FILES := ../../../external/koush/Superuser/Superuser/jni/su/su.c ../../../external/koush/Superuser/Superuser/jni/su/activity.c ../../../external/koush/Superuser/Superuser/jni/su/utils.c dbstub.c +LOCAL_SRC_FILES := ../../../external/koush/Superuser/Superuser/jni/su/su.c ../../../external/koush/Superuser/Superuser/jni/su/daemon.c ../../../external/koush/Superuser/Superuser/jni/su/activity.c ../../../external/koush/Superuser/Superuser/jni/su/utils.c dbstub.c LOCAL_CFLAGS := -DSQLITE_OMIT_LOAD_EXTENSION -DREQUESTOR=\"$(SUPERUSER_PACKAGE)\" ifdef SUPERUSER_PACKAGE_PREFIX LOCAL_CFLAGS += -DREQUESTOR_PREFIX=\"$(SUPERUSER_PACKAGE_PREFIX)\" From ebecbc657e3b8f73f112613a3dfe2eef3078c219 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Sat, 27 Jul 2013 21:52:05 -0700 Subject: [PATCH 023/104] Fixes for Superuser Change-Id: I86a280088ee8767e02ba51b72f4d011cf4658f6f --- su/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/su/Android.mk b/su/Android.mk index 6458e8e5d..8a11752d8 100644 --- a/su/Android.mk +++ b/su/Android.mk @@ -8,7 +8,7 @@ LOCAL_MODULE_TAGS := eng debug LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_STATIC_LIBRARIES := libc LOCAL_C_INCLUDES := external/sqlite/dist -LOCAL_SRC_FILES := ../../../external/koush/Superuser/Superuser/jni/su/su.c ../../../external/koush/Superuser/Superuser/jni/su/activity.c ../../../external/koush/Superuser/Superuser/jni/su/utils.c dbstub.c +LOCAL_SRC_FILES := ../../../external/koush/Superuser/Superuser/jni/su/su.c ../../../external/koush/Superuser/Superuser/jni/su/daemon.c ../../../external/koush/Superuser/Superuser/jni/su/activity.c ../../../external/koush/Superuser/Superuser/jni/su/utils.c dbstub.c LOCAL_CFLAGS := -DSQLITE_OMIT_LOAD_EXTENSION -DREQUESTOR=\"$(SUPERUSER_PACKAGE)\" ifdef SUPERUSER_PACKAGE_PREFIX LOCAL_CFLAGS += -DREQUESTOR_PREFIX=\"$(SUPERUSER_PACKAGE_PREFIX)\" From 912b6d90464adc7065302b33e6fe616771cc5180 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Mon, 29 Jul 2013 20:10:17 -0700 Subject: [PATCH 024/104] su installation and detection updates for 4.3 Change-Id: Idce9a6d4ca18ddc9b49029024c26bc114f6d3c15 --- extendedcommands.c | 23 ++++++++++++++--------- su/Android.mk | 7 +++++++ su/install-su.sh | 24 ++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 su/install-su.sh diff --git a/extendedcommands.c b/extendedcommands.c index fb64e759c..8bf922734 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -1588,16 +1588,23 @@ int verify_root_and_recovery() { int ret = 0; struct stat st; - if (0 == lstat("/system/etc/install-recovery.sh", &st)) { - if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { - ui_show_text(1); - ret = 1; - if (confirm_selection("ROM may flash stock recovery on boot. Fix?", "Yes - Disable recovery flash")) { - __system("chmod -x /system/etc/install-recovery.sh"); + // check to see if install-recovery.sh is going to clobber recovery + // install-recovery.sh is also used to run the su daemon on stock rom for 4.3+ + // so verify that doesn't exist... + if (0 != lstat("/system/etc/.installed_su_daemon", &st)) { + // check install-recovery.sh exists and is executable + if (0 == lstat("/system/etc/install-recovery.sh", &st)) { + if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + ui_show_text(1); + ret = 1; + if (confirm_selection("ROM may flash stock recovery on boot. Fix?", "Yes - Disable recovery flash")) { + __system("chmod -x /system/etc/install-recovery.sh"); + } } } } + int exists = 0; if (0 == lstat("/system/bin/su", &st)) { exists = 1; @@ -1629,9 +1636,7 @@ int verify_root_and_recovery() { ui_show_text(1); ret = 1; if (confirm_selection("Root access is missing. Root device?", "Yes - Root device (/system/xbin/su)")) { - __system("cp /sbin/su.recovery /system/xbin/su"); - __system("chmod 6755 /system/xbin/su"); - __system("ln -sf /system/xbin/su /system/bin/su"); + __system("/sbin/install-su.sh"); } } diff --git a/su/Android.mk b/su/Android.mk index 8a11752d8..f086c86ec 100644 --- a/su/Android.mk +++ b/su/Android.mk @@ -17,3 +17,10 @@ LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin include $(BUILD_EXECUTABLE) +include $(CLEAR_VARS) +LOCAL_MODULE := install-su.sh +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := $(LOCAL_MODULE) +include $(BUILD_PREBUILT) diff --git a/su/install-su.sh b/su/install-su.sh new file mode 100644 index 000000000..aad003e4a --- /dev/null +++ b/su/install-su.sh @@ -0,0 +1,24 @@ +#!/sbin/sh +cp /sbin/su.recovery /system/xbin/su +chmod 6755 /system/xbin/su +ln -sf /system/xbin/su /system/bin/su + +# if the system is at least 4.3, and there is no su daemon built in, +# let's try to install it using install-recovery.sh +BUILD_RELEASE_VERSION=$(cat /system/build.prop | grep ro\\.build\\.version\\.release) +IS_43=$(echo $BUILD_RELEASE_VERSION | grep 4\\.3) +if [ -z "$IS_43" -o "$IS_43" \> "4.3" -o "$IS_43" == "4.3" ] +then + # check for rom su daemon before clobbering install-recovery.sh + if [ ! -f "/system/etc/.has_su_daemon" ] + then + echo -n -e 'ui_print Installing Superuser daemon...\n' > /proc/self/fd/$2 + echo -n -e 'ui_print\n' > /proc/self/fd/$2 + cp install-recovery.sh /system/etc/install-recovery.sh + chmod 755 /system/etc/install-recovery.sh + # note that an post install su daemon was installed + # so recovery doesn't freak out and recommend you disable + # the install-recovery.sh execute bit. + touch /system/etc/.installed_su_daemon + fi +fi From ab6769d156417d822c1374a1352625485e9f1e6b Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Mon, 29 Jul 2013 20:19:25 -0700 Subject: [PATCH 025/104] 6035 Change-Id: Iab792171fdbe62fe85ac3084f49cbd38cb172fba --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 35562185d..8c79b7c19 100644 --- a/Android.mk +++ b/Android.mk @@ -38,7 +38,7 @@ RECOVERY_NAME := CWM-based Recovery endif endif -RECOVERY_VERSION := $(RECOVERY_NAME) v6.0.3.4 +RECOVERY_VERSION := $(RECOVERY_NAME) v6.0.3.5 LOCAL_CFLAGS += -DRECOVERY_VERSION="$(RECOVERY_VERSION)" RECOVERY_API_VERSION := 2 From a2aac8378215633a32966966f518077529e994c4 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Mon, 29 Jul 2013 20:30:55 -0700 Subject: [PATCH 026/104] add su daemon runner Change-Id: Ibb9be9e764d0ccb842a26a0795a0bb6cb6113b58 --- su/Android.mk | 8 ++++++++ su/install-su.sh | 4 +--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/su/Android.mk b/su/Android.mk index f086c86ec..484d21d47 100644 --- a/su/Android.mk +++ b/su/Android.mk @@ -24,3 +24,11 @@ LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin LOCAL_SRC_FILES := $(LOCAL_MODULE) include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := run-su-daemon.sh +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := ../../../external/koush/Superuser/Superuser/assets/install-recovery.sh +include $(BUILD_PREBUILT) diff --git a/su/install-su.sh b/su/install-su.sh index aad003e4a..1ea42882f 100644 --- a/su/install-su.sh +++ b/su/install-su.sh @@ -12,9 +12,7 @@ then # check for rom su daemon before clobbering install-recovery.sh if [ ! -f "/system/etc/.has_su_daemon" ] then - echo -n -e 'ui_print Installing Superuser daemon...\n' > /proc/self/fd/$2 - echo -n -e 'ui_print\n' > /proc/self/fd/$2 - cp install-recovery.sh /system/etc/install-recovery.sh + cp /sbin/run-su-daemon.sh /system/etc/install-recovery.sh chmod 755 /system/etc/install-recovery.sh # note that an post install su daemon was installed # so recovery doesn't freak out and recommend you disable From 04f02c55ed014ef91c0e201bf058a19eeea7db99 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Mon, 29 Jul 2013 21:45:15 -0700 Subject: [PATCH 027/104] 4.3 detection fix Change-Id: Ib5e3df51fa7a383f3699968e899e06d579480564 --- su/install-su.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/su/install-su.sh b/su/install-su.sh index 1ea42882f..878b53fb0 100644 --- a/su/install-su.sh +++ b/su/install-su.sh @@ -7,8 +7,10 @@ ln -sf /system/xbin/su /system/bin/su # let's try to install it using install-recovery.sh BUILD_RELEASE_VERSION=$(cat /system/build.prop | grep ro\\.build\\.version\\.release) IS_43=$(echo $BUILD_RELEASE_VERSION | grep 4\\.3) -if [ -z "$IS_43" -o "$IS_43" \> "4.3" -o "$IS_43" == "4.3" ] +if [ ! -z "$IS_43" ] then + if [ -o "$IS_43" \> "4.3" -o "$IS_43" == "4.3" ] + then # check for rom su daemon before clobbering install-recovery.sh if [ ! -f "/system/etc/.has_su_daemon" ] then @@ -19,4 +21,5 @@ then # the install-recovery.sh execute bit. touch /system/etc/.installed_su_daemon fi + fi fi From 37566e298672adab0370f24dfe5d108053df0f71 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Wed, 31 Jul 2013 01:18:46 -0700 Subject: [PATCH 028/104] unset immutable bit on install-recovery.sh Change-Id: Ie7e8bfea57bed207c3c2d57bf3cd23daf6cd4c20 --- su/install-su.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/su/install-su.sh b/su/install-su.sh index 878b53fb0..136b09e3d 100644 --- a/su/install-su.sh +++ b/su/install-su.sh @@ -14,12 +14,13 @@ then # check for rom su daemon before clobbering install-recovery.sh if [ ! -f "/system/etc/.has_su_daemon" ] then - cp /sbin/run-su-daemon.sh /system/etc/install-recovery.sh - chmod 755 /system/etc/install-recovery.sh - # note that an post install su daemon was installed - # so recovery doesn't freak out and recommend you disable - # the install-recovery.sh execute bit. - touch /system/etc/.installed_su_daemon + chattr -i /system/etc/install-recovery.sh + cp /sbin/run-su-daemon.sh /system/etc/install-recovery.sh + chmod 755 /system/etc/install-recovery.sh + # note that an post install su daemon was installed + # so recovery doesn't freak out and recommend you disable + # the install-recovery.sh execute bit. + touch /system/etc/.installed_su_daemon fi fi fi From 00857c9023a37a65f84df7c6540e24914201d33d Mon Sep 17 00:00:00 2001 From: Steven Luo Date: Wed, 31 Jul 2013 14:12:16 -0700 Subject: [PATCH 029/104] Don't treat link-time warnings as errors when linking with libbusybox See http://review.cyanogenmod.org/#/c/46642/ for why this is necessary. Change-Id: Ib97484755569fcafc062d3c39b2841581987ca72 --- Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/Android.mk b/Android.mk index 515b6535f..b9948225a 100644 --- a/Android.mk +++ b/Android.mk @@ -94,6 +94,7 @@ LOCAL_STATIC_LIBRARIES += libmake_ext4fs libext4_utils_static libz libsparse_sta LOCAL_STATIC_LIBRARIES += libminzip libunz libmincrypt LOCAL_STATIC_LIBRARIES += libminizip libminadbd libedify libbusybox libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image +LOCAL_LDFLAGS += -Wl,--no-fatal-warnings LOCAL_STATIC_LIBRARIES += libdedupe libcrypto_static libcrecovery libflashutils libmtdutils libmmcutils libbmlutils From 8b26c06003c384b206ffebeb8e5b2cf43ab81b00 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Wed, 31 Jul 2013 21:09:58 -0700 Subject: [PATCH 030/104] Fix roots.c to use fs_mgr Change-Id: Ie68daba5e014dff0c4998da85576f35abe8ad270 --- Android.mk | 10 +- bootloader.c | 42 ++++----- common.h | 29 +----- extendedcommands.c | 30 +++--- minadbd/Android.mk | 2 + nandroid.c | 24 ++--- roots.c | 224 +++++++++++++-------------------------------- 7 files changed, 123 insertions(+), 238 deletions(-) diff --git a/Android.mk b/Android.mk index b9948225a..7f42877f8 100644 --- a/Android.mk +++ b/Android.mk @@ -63,7 +63,7 @@ BOARD_RECOVERY_CHAR_HEIGHT := $(shell echo $(BOARD_USE_CUSTOM_RECOVERY_FONT) | c LOCAL_CFLAGS += -DBOARD_RECOVERY_CHAR_WIDTH=$(BOARD_RECOVERY_CHAR_WIDTH) -DBOARD_RECOVERY_CHAR_HEIGHT=$(BOARD_RECOVERY_CHAR_HEIGHT) -BOARD_RECOVERY_DEFINES := BOARD_HAS_NO_SELECT_BUTTON BOARD_UMS_LUNFILE BOARD_RECOVERY_ALWAYS_WIPES BOARD_RECOVERY_HANDLES_MOUNT BOARD_TOUCH_RECOVERY RECOVERY_EXTEND_NANDROID_MENU TARGET_USE_CUSTOM_LUN_FILE_PATH TARGET_DEVICE +BOARD_RECOVERY_DEFINES := BOARD_HAS_NO_SELECT_BUTTON BOARD_UMS_LUNFILE BOARD_RECOVERY_ALWAYS_WIPES BOARD_RECOVERY_HANDLES_MOUNT BOARD_TOUCH_RECOVERY RECOVERY_EXTEND_NANDROID_MENU TARGET_USE_CUSTOM_LUN_FILE_PATH TARGET_DEVICE TARGET_RECOVERY_FSTAB $(foreach board_define,$(BOARD_RECOVERY_DEFINES), \ $(if $($(board_define)), \ @@ -74,7 +74,7 @@ $(foreach board_define,$(BOARD_RECOVERY_DEFINES), \ LOCAL_STATIC_LIBRARIES := LOCAL_CFLAGS += -DUSE_EXT4 -LOCAL_C_INCLUDES += system/extras/ext4_utils +LOCAL_C_INCLUDES += system/extras/ext4_utils system/core/fs_mgr/include LOCAL_STATIC_LIBRARIES += libext4_utils_static libz libsparse_static # This binary is in the recovery ramdisk, which is otherwise a copy of root. @@ -96,7 +96,7 @@ LOCAL_STATIC_LIBRARIES += libminzip libunz libmincrypt LOCAL_STATIC_LIBRARIES += libminizip libminadbd libedify libbusybox libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image LOCAL_LDFLAGS += -Wl,--no-fatal-warnings -LOCAL_STATIC_LIBRARIES += libdedupe libcrypto_static libcrecovery libflashutils libmtdutils libmmcutils libbmlutils +LOCAL_STATIC_LIBRARIES += libfs_mgr libdedupe libcrypto_static libcrecovery libflashutils libmtdutils libmmcutils libbmlutils ifeq ($(BOARD_USES_BML_OVER_MTD),true) LOCAL_STATIC_LIBRARIES += libbml_over_mtd @@ -107,8 +107,6 @@ LOCAL_STATIC_LIBRARIES += libstdc++ libc LOCAL_STATIC_LIBRARIES += libselinux -LOCAL_C_INCLUDES += system/extras/ext4_utils - include $(BUILD_EXECUTABLE) RECOVERY_LINKS := bu make_ext4fs edify busybox flash_image dump_image mkyaffs2image unyaffs erase_image nandroid reboot volume setprop getprop dedupe minizip @@ -157,6 +155,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := verifier_test.c verifier.c +LOCAL_C_INCLUDES += system/extras/ext4_utils system/core/fs_mgr/include + LOCAL_MODULE := verifier_test LOCAL_FORCE_STATIC_EXECUTABLE := true diff --git a/bootloader.c b/bootloader.c index e88160d8c..588ea5560 100644 --- a/bootloader.c +++ b/bootloader.c @@ -71,22 +71,22 @@ 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; @@ -97,22 +97,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; @@ -120,16 +120,16 @@ 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; } @@ -161,20 +161,20 @@ static void wait_for_device(const char* 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)); @@ -183,19 +183,19 @@ 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->device, strerror(errno)); + LOGE("Failed closing %s\n(%s)\n", v->blk_device, strerror(errno)); return -1; } return 0; diff --git a/common.h b/common.h index bf81b48ad..195d67ab0 100644 --- a/common.h +++ b/common.h @@ -18,6 +18,7 @@ #define RECOVERY_COMMON_H #include +#include // Initialize the graphics system. void ui_init(); @@ -110,33 +111,7 @@ void ui_reset_progress(); #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). - - const char* fs_type2; - - const char* fs_options; - - const char* fs_options2; - - const char* lun; // (/sdcard, /emmc, /external_sd only) LUN file to - // use when mounting via USB mass storage -} Volume; +typedef struct fstab_rec Volume; typedef struct { // number of frames in indeterminate progress bar animation diff --git a/extendedcommands.c b/extendedcommands.c index 8bf922734..7e6c2f9a3 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -458,7 +458,7 @@ static struct lun_node *lun_head = NULL; static struct lun_node *lun_tail = NULL; int control_usb_storage_set_lun(Volume* vol, bool enable, const char *lun_file) { - const char *vol_device = enable ? vol->device : ""; + const char *vol_device = enable ? vol->blk_device : ""; int fd; struct lun_node *node; @@ -471,7 +471,7 @@ int control_usb_storage_set_lun(Volume* vol, bool enable, const char *lun_file) } // Open a handle to the LUN file - LOGI("Trying %s on LUN file %s\n", vol->device, lun_file); + LOGI("Trying %s on LUN file %s\n", vol->blk_device, lun_file); if ((fd = open(lun_file, O_WRONLY)) < 0) { LOGW("Unable to open ums lunfile %s (%s)\n", lun_file, strerror(errno)); return -1; @@ -479,7 +479,7 @@ int control_usb_storage_set_lun(Volume* vol, bool enable, const char *lun_file) // Write the volume path to the LUN file if ((write(fd, vol_device, strlen(vol_device) + 1) < 0) && - (!enable || !vol->device2 || (write(fd, vol->device2, strlen(vol->device2)) < 0))) { + (!enable || !vol->blk_device2 || (write(fd, vol->blk_device2, strlen(vol->blk_device2)) < 0))) { LOGW("Unable to write to ums lunfile %s (%s)\n", lun_file, strerror(errno)); close(fd); return -1; @@ -498,7 +498,7 @@ int control_usb_storage_set_lun(Volume* vol, bool enable, const char *lun_file) lun_tail = node; } - LOGI("Successfully %sshared %s on LUN file %s\n", enable ? "" : "un", vol->device, lun_file); + LOGI("Successfully %sshared %s on LUN file %s\n", enable ? "" : "un", vol->blk_device, lun_file); return 0; } } @@ -544,7 +544,7 @@ int control_usb_storage_for_lun(Volume* vol, bool enable) { } // All LUNs were exhausted and none worked - LOGW("Could not %sable %s on LUN %d\n", enable ? "en" : "dis", vol->device, lun_num); + LOGW("Could not %sable %s on LUN %d\n", enable ? "en" : "dis", vol->blk_device, lun_num); return -1; // -1 failure, 0 success } @@ -681,7 +681,7 @@ int format_device(const char *device, const char *path, const char *fs_type) { } if (strcmp(v->mount_point, path) != 0) { - return format_unknown_device(v->device, path, NULL); + return format_unknown_device(v->blk_device, path, NULL); } if (ensure_path_unmounted(path) != 0) { @@ -742,7 +742,7 @@ int format_unknown_device(const char *device, const char* path, const char *fs_t { struct stat st; Volume *vol = volume_for_path("/sd-ext"); - if (vol == NULL || 0 != stat(vol->device, &st)) + if (vol == NULL || 0 != stat(vol->blk_device, &st)) { ui_print("No app2sd partition found. Skipping format of /sd-ext.\n"); return 0; @@ -1275,7 +1275,7 @@ static void partition_sdcard(const char* volume) { char sddevice[256]; Volume *vol = volume_for_path(volume); - strcpy(sddevice, vol->device); + strcpy(sddevice, vol->blk_device); // we only want the mmcblk, not the partition sddevice[strlen("/dev/block/mmcblkX")] = NULL; char cmd[PATH_MAX]; @@ -1295,10 +1295,10 @@ int can_partition(const char* volume) { return 0; } - int vol_len = strlen(vol->device); + int vol_len = strlen(vol->blk_device); // do not allow partitioning of a device that isn't mmcblkX or mmcblkXp1 - if (vol->device[vol_len - 2] == 'p' && vol->device[vol_len - 1] != '1') { - LOGI("Can't partition unsafe device: %s\n", vol->device); + if (vol->blk_device[vol_len - 2] == 'p' && vol->blk_device[vol_len - 1] != '1') { + LOGI("Can't partition unsafe device: %s\n", vol->blk_device); return 0; } @@ -1432,10 +1432,10 @@ void write_fstab_root(char *path, FILE *file) } char device[200]; - if (vol->device[0] != '/') - get_partition_device(vol->device, device); + if (vol->blk_device[0] != '/') + get_partition_device(vol->blk_device, device); else - strcpy(device, vol->device); + strcpy(device, vol->blk_device); fprintf(file, "%s ", device); fprintf(file, "%s ", path); @@ -1483,7 +1483,7 @@ int bml_check_volume(const char *path) { ui_print("%s may be rfs. Checking...\n", path); char tmp[PATH_MAX]; - sprintf(tmp, "mount -t rfs %s %s", vol->device, path); + sprintf(tmp, "mount -t rfs %s %s", vol->blk_device, path); int ret = __system(tmp); printf("%d\n", ret); return ret == 0 ? 1 : 0; diff --git a/minadbd/Android.mk b/minadbd/Android.mk index 5a4de6828..5734f875c 100644 --- a/minadbd/Android.mk +++ b/minadbd/Android.mk @@ -25,6 +25,8 @@ LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE LOCAL_MODULE := libminadbd +LOCAL_C_INCLUDES += system/extras/ext4_utils system/core/fs_mgr/include + LOCAL_STATIC_LIBRARIES := libcutils libc include $(BUILD_STATIC_LIBRARY) diff --git a/nandroid.c b/nandroid.c index 32b93f8b2..f86bce5eb 100644 --- a/nandroid.c +++ b/nandroid.c @@ -328,7 +328,7 @@ int nandroid_backup_partition(const char* backup_path, const char* root) { sprintf(tmp, "%s/%s.img", backup_path, name); ui_print("Backing up %s image...\n", name); - if (0 != (ret = backup_raw_partition(vol->fs_type, vol->device, tmp))) { + if (0 != (ret = backup_raw_partition(vol->fs_type, vol->blk_device, tmp))) { ui_print("Error while backing up %s image!", name); return ret; } @@ -350,11 +350,13 @@ int nandroid_backup(const char* backup_path) return print_and_error("Can't mount backup path.\n"); } - Volume* volume = volume_for_path(backup_path); + Volume* volume; + if (is_data_media()) + volume = volume_for_path("/data"); + else + volume = volume_for_path(backup_path); if (NULL == volume) return print_and_error("Unable to find volume for backup path.\n"); - if (is_data_media_volume_path(volume->mount_point)) - volume = volume_for_path("/data"); int ret; struct statfs sfs; struct stat s; @@ -379,14 +381,14 @@ int nandroid_backup(const char* backup_path) return ret; Volume *vol = volume_for_path("/wimax"); - if (vol != NULL && 0 == stat(vol->device, &s)) + if (vol != NULL && 0 == stat(vol->blk_device, &s)) { char serialno[PROPERTY_VALUE_MAX]; ui_print("Backing up WiMAX...\n"); serialno[0] = 0; property_get("ro.serialno", serialno, ""); sprintf(tmp, "%s/wimax.%s.img", backup_path, serialno); - ret = backup_raw_partition(vol->fs_type, vol->device, tmp); + ret = backup_raw_partition(vol->fs_type, vol->blk_device, tmp); if (0 != ret) return print_and_error("Error while dumping WiMAX image!\n"); } @@ -414,7 +416,7 @@ int nandroid_backup(const char* backup_path) return ret; vol = volume_for_path("/sd-ext"); - if (vol == NULL || 0 != stat(vol->device, &s)) + if (vol == NULL || 0 != stat(vol->blk_device, &s)) { ui_print("No sd-ext found. Skipping backup of sd-ext.\n"); } @@ -587,7 +589,7 @@ int nandroid_restore_partition_extended(const char* backup_path, const char* mou Volume *vol = volume_for_path(mount_point); const char *device = NULL; if (vol != NULL) - device = vol->device; + device = vol->blk_device; char tmp[PATH_MAX]; sprintf(tmp, "%s/%s.img", backup_path, name); @@ -721,7 +723,7 @@ int nandroid_restore_partition(const char* backup_path, const char* root) { sprintf(tmp, "%s%s.img", backup_path, root); ui_print("Restoring %s image...\n", name); - if (0 != (ret = restore_raw_partition(vol->fs_type, vol->device, tmp))) { + if (0 != (ret = restore_raw_partition(vol->fs_type, vol->blk_device, tmp))) { ui_print("Error while flashing %s image!", name); return ret; } @@ -753,7 +755,7 @@ int nandroid_restore(const char* backup_path, int restore_boot, int restore_syst struct stat s; Volume *vol = volume_for_path("/wimax"); - if (restore_wimax && vol != NULL && 0 == stat(vol->device, &s)) + if (restore_wimax && vol != NULL && 0 == stat(vol->blk_device, &s)) { char serialno[PROPERTY_VALUE_MAX]; @@ -775,7 +777,7 @@ int nandroid_restore(const char* backup_path, int restore_boot, int restore_syst if (0 != (ret = format_volume("/wimax"))) return print_and_error("Error while formatting wimax!\n"); ui_print("Restoring WiMAX image...\n"); - if (0 != (ret = restore_raw_partition(vol->fs_type, vol->device, tmp))) + if (0 != (ret = restore_raw_partition(vol->fs_type, vol->blk_device, tmp))) return ret; } } diff --git a/roots.c b/roots.c index 44fa14ae6..7b9460b58 100644 --- a/roots.c +++ b/roots.c @@ -28,156 +28,51 @@ #include "common.h" #include "make_ext4fs.h" +#include +#include #include "flashutils/flashutils.h" #include "extendedcommands.h" -int num_volumes; -Volume* device_volumes; +static struct fstab *fstab = NULL; int get_num_volumes() { - return num_volumes; + return fstab->num_entries; } Volume* get_device_volumes() { - return device_volumes; -} - -static int is_null(const char* sz) { - if (sz == NULL) - return 1; - if (strcmp("NULL", sz) == 0) - return 1; - return 0; -} - -static char* dupe_string(const char* sz) { - if (is_null(sz)) - return NULL; - return strdup(sz); -} - -static int parse_options(char* options, Volume* volume) { - char* option; - while (option = strtok(options, ",")) { - options = NULL; - - if (strncmp(option, "length=", 7) == 0) { - volume->length = strtoll(option+7, NULL, 10); - } else if (strncmp(option, "fstype2=", 8) == 0) { - volume->fs_type2 = volume->fs_type; - volume->fs_type = strdup(option + 8); - } else if (strncmp(option, "fs_options=", 11) == 0) { - volume->fs_options = strdup(option + 11); - } else if (strncmp(option, "fs_options2=", 12) == 0) { - volume->fs_options2 = strdup(option + 12); - } else if (strncmp(option, "lun=", 4) == 0) { - volume->lun = strdup(option + 4); - } else { - LOGE("bad option \"%s\"\n", option); - return -1; - } - } - return 0; + return fstab->recs; } void load_volume_table() { - int alloc = 2; - device_volumes = 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].fs_type2 = NULL; - device_volumes[0].fs_options = NULL; - device_volumes[0].fs_options2 = NULL; - device_volumes[0].lun = NULL; - device_volumes[0].length = 0; - num_volumes = 1; - - FILE* fstab = fopen("/etc/recovery.fstab", "r"); - if (fstab == NULL) { - LOGE("failed to open /etc/recovery.fstab (%s)\n", strerror(errno)); - 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; - } - } + int ret; - if (mount_point && fs_type && device) { - while (num_volumes >= alloc) { - alloc *= 2; - device_volumes = 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; - - device_volumes[num_volumes].fs_type2 = NULL; - device_volumes[num_volumes].fs_options = NULL; - device_volumes[num_volumes].fs_options2 = NULL; - device_volumes[num_volumes].lun = NULL; - - 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); + fstab = fs_mgr_read_fstab("/etc/recovery.fstab"); + if (!fstab) { + LOGE("failed to read /etc/recovery.fstab\n"); + return; } - fclose(fstab); + 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; + } fprintf(stderr, "recovery filesystem table\n"); fprintf(stderr, "=========================\n"); - for (i = 0; i < num_volumes; ++i) { - Volume* v = &device_volumes[i]; - fprintf(stderr, " %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]; + fprintf(stderr, " %d %s %s %s %lld\n", i, v->mount_point, v->fs_type, + v->blk_device, v->length); } - fprintf(stderr,"\n"); + fprintf(stderr, "\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 try_mount(const char* device, const char* mount_point, const char* fs_type, const char* fs_options) { @@ -201,30 +96,43 @@ int try_mount(const char* device, const char* mount_point, const char* fs_type, int is_data_media() { int i; - for (i = 0; i < num_volumes; i++) { - Volume* vol = device_volumes + i; + int has_sdcard = 0; + for (i = 0; i < get_num_volumes(); i++) { + Volume* vol = get_device_volumes() + i; if (strcmp(vol->fs_type, "datamedia") == 0) return 1; + if (strcmp(vol->mount_point, "/sdcard") == 0) + has_sdcard = 1; } - return 0; + return !has_sdcard; } void setup_data_media() { int i; - for (i = 0; i < num_volumes; i++) { - Volume* vol = device_volumes + i; + char* mount_point = "/sdcard"; + for (i = 0; i < get_num_volumes(); i++) { + Volume* vol = get_device_volumes() + i; if (strcmp(vol->fs_type, "datamedia") == 0) { - rmdir(vol->mount_point); - mkdir("/data/media", 0755); - symlink("/data/media", vol->mount_point); - return; + mount_point = vol->mount_point; + break; } } + unlink(mount_point); + rmdir(mount_point); + mkdir("/data/media", 0755); + symlink("/data/media", mount_point); } int is_data_media_volume_path(const char* path) { Volume* v = volume_for_path(path); - return strcmp(v->fs_type, "datamedia") == 0; + if (v != NULL) + return strcmp(v->fs_type, "datamedia") == 0; + + if (!is_data_media()) { + return 0; + } + + return strcmp(path, "/sdcard") == 0 || path == strstr(path, "/sdcard/"); } int ensure_path_mounted(const char* path) { @@ -232,11 +140,6 @@ int ensure_path_mounted(const char* path) { } int ensure_path_mounted_at_mount_point(const char* path, const char* mount_point) { - Volume* v = volume_for_path(path); - if (v == NULL) { - LOGE("unknown volume for path [%s]\n", path); - return -1; - } if (is_data_media_volume_path(path)) { if (ui_should_log_stdout()) { LOGI("using /data/media for %s.\n", path); @@ -247,6 +150,11 @@ int ensure_path_mounted_at_mount_point(const char* path, const char* mount_point setup_data_media(); return 0; } + Volume* v = volume_for_path(path); + if (v == NULL) { + LOGE("unknown volume for path [%s]\n", path); + return -1; + } if (strcmp(v->fs_type, "ramdisk") == 0) { // the ramdisk is always mounted. return 0; @@ -275,10 +183,10 @@ int ensure_path_mounted_at_mount_point(const char* path, const char* mount_point // 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, mount_point); + v->blk_device, mount_point); return -1; } return mtd_mount_partition(partition, mount_point, v->fs_type, 0); @@ -286,13 +194,11 @@ int ensure_path_mounted_at_mount_point(const char* path, const char* mount_point strcmp(v->fs_type, "ext3") == 0 || strcmp(v->fs_type, "rfs") == 0 || strcmp(v->fs_type, "vfat") == 0) { - if ((result = try_mount(v->device, mount_point, v->fs_type, v->fs_options)) == 0) - return 0; - if ((result = try_mount(v->device2, mount_point, v->fs_type, v->fs_options)) == 0) + if ((result = try_mount(v->blk_device, mount_point, v->fs_type, v->fs_options)) == 0) return 0; - if ((result = try_mount(v->device, mount_point, v->fs_type2, v->fs_options2)) == 0) + if ((result = try_mount(v->blk_device, mount_point, v->fs_type2, v->fs_options2)) == 0) return 0; - if ((result = try_mount(v->device2, mount_point, v->fs_type2, v->fs_options2)) == 0) + if ((result = try_mount(v->blk_device2, mount_point, v->fs_type2, v->fs_options2)) == 0) return 0; return result; } else { @@ -372,7 +278,7 @@ int format_volume(const char* volume) { LOGE("can't give path \"%s\" to format_volume\n", volume); return -1; #endif - return format_unknown_device(v->device, volume, NULL); + return format_unknown_device(v->blk_device, volume, NULL); } if (ensure_path_unmounted(volume) != 0) { @@ -382,31 +288,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) { - int result = make_ext4fs(v->device, v->length, volume, sehandle); + 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; @@ -416,7 +322,7 @@ int format_volume(const char* volume) { LOGE("format_volume: fs_type \"%s\" unsupported\n", v->fs_type); return -1; #endif - return format_unknown_device(v->device, volume, v->fs_type); + return format_unknown_device(v->blk_device, volume, v->fs_type); } void handle_data_media_format(int handle) { From d9b4033df5bddf2d879edd91772e3e08cf9c5eb0 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Fri, 2 Nov 2012 15:04:05 -0700 Subject: [PATCH 031/104] move key loading to verifier code Add an option to verifier_test to load keys from a file, the way the recovery does. Conflicts: verifier_test.c Change-Id: Icba0e391164f2c1a9fefeab4b0bcb878e91d17b4 --- install.c | 100 ---------------------------------------------- verifier.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ verifier.h | 2 + verifier_test.c | 11 ++++-- 4 files changed, 113 insertions(+), 103 deletions(-) diff --git a/install.c b/install.c index a6a0cf104..95cd48a39 100644 --- a/install.c +++ b/install.c @@ -260,106 +260,6 @@ try_update_binary(const char *path, ZipArchive *zip) { 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}}" -// -// 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. -static 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); - - 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", - &(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; - } - - LOGI("read key e=%d\n", key->exponent); - } - } - - fclose(f); - return out; - -exit: - if (f) fclose(f); - free(out); - *numKeys = 0; - return NULL; -} - static int really_install_package(const char *path) { diff --git a/verifier.c b/verifier.c index 729e085cf..01be98d6e 100644 --- a/verifier.c +++ b/verifier.c @@ -23,6 +23,7 @@ #include #include #include +#include // 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 @@ -176,9 +177,111 @@ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKey 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); } } free(eocd); 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); + + 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", + &(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; + } + + LOGI("read key e=%d\n", key->exponent); + } + } + + fclose(f); + return out; + +exit: + if (f) fclose(f); + free(out); + *numKeys = 0; + return NULL; +} diff --git a/verifier.h b/verifier.h index 1bdfca6dd..e9ef3b722 100644 --- a/verifier.h +++ b/verifier.h @@ -24,6 +24,8 @@ */ int verify_file(const char* path, const RSAPublicKey *pKeys, unsigned int numKeys); +RSAPublicKey* load_keys(const char* filename, int* numKeys); + #define VERIFY_SUCCESS 0 #define VERIFY_FAILURE 1 diff --git a/verifier_test.c b/verifier_test.c index b303ede6f..d23b3a4a4 100644 --- a/verifier_test.c +++ b/verifier_test.c @@ -110,19 +110,24 @@ void ui_set_progress(float fraction) { } int main(int argc, char **argv) { - if (argc != 2 && argc != 3) { - fprintf(stderr, "Usage: %s [-f4] \n", argv[0]); + if (argc < 2 || argc > 4) { + fprintf(stderr, "Usage: %s [-f4 | -file ] \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; } - int result = verify_file(*argv, key, 1); + int result = verify_file(*argv, key, num_keys); if (result == VERIFY_SUCCESS) { printf("SUCCESS\n"); return 0; From 2ad4587700a7f714ca8d4efd27e09cf09fe78efa Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 16 May 2013 11:23:48 -0700 Subject: [PATCH 032/104] recovery: save logs from the last few invocations of recovery Extends the last_log mechanism to save logs from the last six invocations of recovery, so that we're more likely to have useful logs even if the device has repeatedly booted into recovery. Conflicts: recovery.c Change-Id: I08ae7a09553ada45f9e0733fe1e55e5a22efd9f9 --- recovery.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/recovery.c b/recovery.c index 9e54147b1..0e6331b5d 100644 --- a/recovery.c +++ b/recovery.c @@ -61,10 +61,11 @@ static const struct option OPTIONS[] = { { 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 *CACHE_ROOT = "/cache"; static const char *SDCARD_ROOT = "/sdcard"; static int allow_display_toggle = 0; @@ -273,6 +274,21 @@ copy_log_file(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 @@ -870,6 +886,8 @@ main(int argc, char **argv) { load_volume_table(); process_volumes(); LOGI("Processing arguments.\n"); + ensure_path_mounted(LAST_LOG_FILE); + rotate_last_logs(5); get_args(&argc, &argv); int previous_runs = 0; From 4dc0a95684364596982b5562e4469f95b25d998b Mon Sep 17 00:00:00 2001 From: Chirayu Desai Date: Sun, 4 Aug 2013 15:37:26 +0530 Subject: [PATCH 033/104] Revert "Use fstab v1 as a default." This reverts commit 1a6c91fcbc16f90b3dcbc9cef3e1052c84a40ffb. Change-Id: I5fb1105bfb640f0552ddcc4a99b6f98487a1c946 --- Android.mk | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Android.mk b/Android.mk index 7f42877f8..6dbfb9f93 100644 --- a/Android.mk +++ b/Android.mk @@ -29,9 +29,7 @@ LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true -ifeq ($(RECOVERY_FSTAB_VERSION),) -RECOVERY_FSTAB_VERSION := 1 -endif +RECOVERY_FSTAB_VERSION := 2 ifdef I_AM_KOUSH RECOVERY_NAME := ClockworkMod Recovery From c70c20232fefb8c583943b2b6fb725fc0dd47444 Mon Sep 17 00:00:00 2001 From: Pawit Pornkitprasan Date: Sat, 3 Aug 2013 21:21:18 +0700 Subject: [PATCH 034/104] recovery: ignore voldmanaged volumes Voldmanaged volumes use sysfs path instead of device path which is not supported by recovery, so a separate entries for /sdcard and /external_sd are required. We ignore these volume so we don't show the menu for "mount /storage/sdcard0", etc. Change-Id: I6bf26e0ad3fb59579c986cf5537f9e70448891e9 --- extendedcommands.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extendedcommands.c b/extendedcommands.c index 7e6c2f9a3..a17208b15 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -873,6 +873,9 @@ void show_partition_menu() for (i = 0; i < num_volumes; ++i) { Volume* v = &device_volumes[i]; + + if (fs_mgr_is_voldmanaged(v)) continue; + if(strcmp("ramdisk", v->fs_type) != 0 && strcmp("mtd", v->fs_type) != 0 && strcmp("emmc", v->fs_type) != 0 && strcmp("bml", v->fs_type) != 0) { if (strcmp("datamedia", v->fs_type) != 0) { sprintf(&mount_menu[mountable_volumes].mount, "mount %s", v->mount_point); From 935a50437946b8d0d083eb815b8ef575d9770fe5 Mon Sep 17 00:00:00 2001 From: Michael Bestas Date: Fri, 12 Jul 2013 14:00:19 +0300 Subject: [PATCH 035/104] Update wipe data option confirmation This commit allows the wipe data/factory reset option to recognize the .one_confirm or .no_confirm files in the clockworkmod folder. Signed-off-by: Michael Bestas Change-Id: Ib0f82b4448e3b9df95a5e59a49428f3e9da58578 --- recovery.c | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/recovery.c b/recovery.c index 0e6331b5d..560b1f950 100644 --- a/recovery.c +++ b/recovery.c @@ -654,35 +654,8 @@ update_directory(const char* path, const char* unmount_when_done) { static void wipe_data(int confirm) { - if (confirm) { - static char** title_headers = NULL; - - if (title_headers == NULL) { - char* headers[] = { "Confirm wipe of all user data?", - " THIS CAN NOT BE UNDONE.", - "", - NULL }; - title_headers = prepend_title((const char**)headers); - } - - char* items[] = { " No", - " No", - " No", - " No", - " No", - " No", - " No", - " Yes -- delete all user data", // [7] - " No", - " No", - " No", - NULL }; - - int chosen_item = get_menu_selection(title_headers, items, 1, 0); - if (chosen_item != 7) { - return; - } - } + if (confirm && !confirm_selection( "Confirm wipe of all user data?", "Yes - Wipe all user data")) + return; ui_print("\n-- Wiping data...\n"); device_wipe_data(); From 7e87f26ac10da24b7ece41a109021c17484055ac Mon Sep 17 00:00:00 2001 From: philz-cwm6 Date: Wed, 12 Jun 2013 15:54:08 +0200 Subject: [PATCH 036/104] Add sdparted option to partition in ext4 fstype Taken from PhilZ Touch recovery Signed-off-by: Michael Bestas Change-Id: I079617fc32f8ad0aea754a60898cd4f9cc482524 --- extendedcommands.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/extendedcommands.c b/extendedcommands.c index a17208b15..02abf0ab4 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -1265,8 +1265,14 @@ static void partition_sdcard(const char* volume) { "256M", NULL }; + static char* partition_types[] = { "ext3", + "ext4", + NULL + }; + static char* ext_headers[] = { "Ext Size", "", NULL }; static char* swap_headers[] = { "Swap Size", "", NULL }; + static char* fstype_headers[] = {"Partition Type", "", NULL }; int ext_size = get_menu_selection(ext_headers, ext_sizes, 0, 0); if (ext_size == GO_BACK) @@ -1276,6 +1282,10 @@ static void partition_sdcard(const char* volume) { if (swap_size == GO_BACK) return; + int partition_type = get_menu_selection(fstype_headers, partition_types, 0, 0); + if (partition_type == GO_BACK) + return; + char sddevice[256]; Volume *vol = volume_for_path(volume); strcpy(sddevice, vol->blk_device); @@ -1283,7 +1293,7 @@ static void partition_sdcard(const char* volume) { sddevice[strlen("/dev/block/mmcblkX")] = NULL; char cmd[PATH_MAX]; setenv("SDPATH", sddevice, 1); - sprintf(cmd, "sdparted -es %s -ss %s -efs ext3 -s", ext_sizes[ext_size], swap_sizes[swap_size]); + sprintf(cmd, "sdparted -es %s -ss %s -efs %s -s", ext_sizes[ext_size], swap_sizes[swap_size], partition_types[partition_type]); ui_print("Partitioning SD Card... please wait...\n"); if (0 == __system(cmd)) ui_print("Done!\n"); From 985324fb7dac992e8ddb0248c8af948312a9be75 Mon Sep 17 00:00:00 2001 From: philz-cwm6 Date: Tue, 6 Aug 2013 22:03:30 +0200 Subject: [PATCH 037/104] unlink() equals remove() in this situation can cause data loss and filesystem corruption if some data is not sync when mount sdcard Change-Id: Ib3ad32842fcbf3b058c986df6901c7744bd5cf1f --- roots.c | 1 - 1 file changed, 1 deletion(-) diff --git a/roots.c b/roots.c index 7b9460b58..fc8dc0f94 100644 --- a/roots.c +++ b/roots.c @@ -117,7 +117,6 @@ void setup_data_media() { break; } } - unlink(mount_point); rmdir(mount_point); mkdir("/data/media", 0755); symlink("/data/media", mount_point); From 3860331960b6e599fe62a8c190702aa312aa1372 Mon Sep 17 00:00:00 2001 From: philz-cwm6 Date: Fri, 9 Aug 2013 10:33:54 +0200 Subject: [PATCH 038/104] fix stat for correct free space when backup path is second storage Change-Id: I762269d696a194889e74c101b9338920e924ebb5 --- nandroid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nandroid.c b/nandroid.c index f86bce5eb..2aec63c0a 100644 --- a/nandroid.c +++ b/nandroid.c @@ -351,7 +351,7 @@ int nandroid_backup(const char* backup_path) } Volume* volume; - if (is_data_media()) + if (is_data_media_volume_path(backup_path)) volume = volume_for_path("/data"); else volume = volume_for_path(backup_path); From 0c328c787c2eb6d2d3091710c588869f666ae332 Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Tue, 13 Aug 2013 14:50:46 -0700 Subject: [PATCH 039/104] Shrink cid Change-Id: I00556341b746f186f424ebe46c94fd02f10e6536 --- res/images/icon_cid.png | Bin 36007 -> 89300 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/res/images/icon_cid.png b/res/images/icon_cid.png index 167ee38b9bfc9ae1e99690155354a8a826f1750f..fbc15f85f4869b08d89110d5d049e1d933e2a50d 100644 GIT binary patch literal 89300 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

XNxk9D~Nk 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

=3%vCg!XL*A7C>m#2skBh$pqE&_B0|0AdVr> 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 B=w|=` literal 36007 zcmZ^LcRbZ!{P5>m*S=(yTqG*8Qk0PuO4%#fWMnJr+L_rZRIYJ_$d)p%5gCycvbVT4 zmvFP5bNfEO*YkRw*X#L%>wZ4xocB2I_xrrp5v8S}N=JQ;8iF9Ydw1{XKoF@f1W|-i zQh+DbdnWYYgUao$(K85QJ45_|*=7C&qoA`M$`3tsU2Q$QEuY#z3Xff_Y`E_^TiV&^ z*jPSBxqr8jfuPvy_wFd@`HZbi%DQpxqeM=Uv%OXL*TQPgKBbVk;v}e|P>qRR4Jo^R z?-@;V4<^ZHaBSYOHaeR*B)UVxW@YoGG1nu$#Wo9f*O#%(Wp32vq7R!41aT=MfJbnd1&TWPu7@viXd4-3QLHu_{_viW<8fqTI)*Ssb+wm;7V zsI?APn;!(?=oq#t{k~L7_%KjX`fAOE%S1d8UM$t+J!+KY%Ia6<#8eJP5RSHfQl5&4 zER~jQAh~f0C?3}<%fc(-%}Dw661w^$H(T1%;nh}8g%Q#S{4KuVPDL|=qf_C6 z>+;1VbW3f_c*O^F4+R!ONhy6r`Ip+*@qaZ~BiGfDy4sQfKJ|bI2^Z_euYpKr!}e{Am10-|E?If@2LR4tE)fV zYnO}!bI79eD)R*YuJbxiB|3<^DGVI3JBuh4}vGh zbs8{voq`iH)?G*ep@Lb-dfgtA*AD3wJ^X|JtOkW32d6zqHnUc*Ll?j9i*e&%ywAp_ z?t$mQ7gp*#cy;fCS6pAL>&b2(~jQ>Ab6*BwA4}7dEYB8-=NS!ScMH-uC&u z^Ok}-b!pLZX&vLsw|M_Oz9xB=?bn|>Q$0?B17fp9P!fNxC3-21p2(9GKK$*ukNBbi zm}sCbjpG1e_Ra;@rMb?Dg@Otauwjp`$SQvsB9T9AaP`~?elF{FxaX`NZ2CZ`sMVx? zAPSpa3Q!W37KE%1dlD@1g+}Lgc1jke37mH1>`C#+wcB=H31euhJ!{mOxe81fY#{MoTqILft1D@vYCVg4;fM{JEEQKod?%FV>>Ez8#>h02q;fyq{-@2BY-_GipCv`?lz|t#>XsKp>(%Q*B}03rq>Qq)#v$q)4sDM4uecq5|&u^sTP`3U>x zsLrq12-c>cil7(fpF?1&5nTl4_VjDnJW}>cG38B(V31W| z_&*^r`oLk)zPXabYFYCt$k02D#UjmP*TSbBY<>x{si*dx@zr$NZXmz2YZrdk9%}-m5z}DpFnVmdh+lBlQF4iObKxIu?G#%N%&H?owg^G1)&E?WN z#%t2dxl+=JqT?&>&(ySUUNB59-$zQJE%T)wDC8|?ZB|UKj*+u?#E!^6QRucH^$d;% z+T^*@G7Gr*{vRJwc{8RSC6PDnPhkgOKs6Xz2gc2STDom}aA3BhXIJuo0btu%{rXQw z`>p~-x@@l2GUTTL*Gh)FOg9u$i}C|)zHcO@nQ}V#+6FT_UBg7+X;{*En^~0+w4`xU zP+lKw0M?hP{tYUR)V_N3YP#}6Ukx~*ObKXoTlno{Wh2{|yh%ts`DdBB-xAQE>m~wh zhd!OMKwZ?Ye<_nl=hC={_Y!6oKmZ*5iF+ygC-}oBN}48CprmJrHlo3os3PXZ@6aY* z2Eh~o?H@ix!4y+=^_C!z zko7?zskjxGr(LKAkcR?8nRRs=?UVt)E?~3r%Uu*VbN^}kXJa+ZfI7fYRq(39=uu4B zgd0(U#){uG{vrodRdz8cU# zp{svy!$-zRt6ZlI$n%>9n;*%*ZoljpC+MP&B=(lBretO!XXH3{8#y9zA&8%1MiQyQE9M1i z?TR<|OHG7B7UC7$-vm_-OtT4eN#{-8Q8at#F$L7LdWj;7((lSdnFo1&!1QC6O%G>; z)woicXi8;$?Mhw*Z8F;?6T_RzqrVX-Gx*;IwN@a2Ih}p#`4ZRf^c@mqF#JfRVz~KP z*^PW>x+lg#LPgq@F?D3$q#K3zeml)C{W;Bj8jpV_I4&Q)CscBQ7uN=b8>jw?Po4hu-5WjM7RWFmRa$UkGmp!vuviW$gbmzz$^a5I-|N;Rz8$6<;? z39|~XIVtsA#F+i%SskqG^=@RUeuTY18ULB);g1@1z6DbT{6Cv!c0oZTjq^H=Mbesa z?gP1$eO!z3!79AzO(l6#Af*Y?Dz`7GGHChMZ+TAkTB@iH*JvyAe@Cn9#AZYhrKhoZ zgbB%^dkxpor|ymEj25{V<%fZp9e$0k8i(U`DF>g&BXhFfX!Why8cRH5T?uBC(#Fmm zmP{Yl{X_(e)~7eRSWOQ=UohEls_(8|1{EF=QVnU&^o_9=-Xo(hHLr5YzzV7Wkw;t4+KA}c;`gN&8&lTW#O3`bnjLk8FX*u3h-B8q*quk-Yh)>-K)9w zEjkOFcV$_`mttci47_3@mt>k#svoN?=oNQgtTrU zrT}x;X4-D^E`Xutl^$KVx+?sUU$|G<@*af9i6#W3YM5Bk(4=(ROFpk_DDO z7=APa49fLRA~4d(x~K~y%1zQ)7ohJ^Ly3OMIAeX_*^us0`=no(@D!hv4z9gvPS(Je z3l296$0qa@lBp*{P=0Y2VbuhA;HyS7up7a{-L$=A_NnZ@&eBxvuGJ77l7?W(%*Oaa z3VG{dw&$B^P2d^#42*jVO!%KqErTYsknjo#5;7*8_DAkHHGwllt0Gv^lnkp|0BaJq z>QAYVFo%O9e5P@BGdWP$? z9D*>dW)I(?*RFt|q%SOy?RDRqond$I`1h}!;Rk?6$s}W~ed?+9+8w09bYh=u`qk3F z&J?rkFlqqaPB0?m{5qj^D;&Q%HA+lm(@+2gN&h_oZu2b=ZX#Bg@jtpqgX`aw!i9)q z#6%WgO&L}VW}0V#Z}14XujucHX&QP_ z#%X)oh08!U8I*2jk$dds7lQ*(Bk&*y03#(Vf>H0U$%}(gcCvg>@bggzq6>My9EyqS zhgrC_3;iGoLFTmS-i6qwMy)Kx0c3~YY&kB0H?z(_qMskW{athoY|fRph9nUCSyNdM zlsr5NrbXVO1~qxIGVIz%|3m3! zF<4Qeymac{iZ&pqfvTqfbYU5&>1&#>NAz%qM_A z2ok#f;A-c!VpMLXJ3N0@AG}rh9!?IKB)}9B zBFeP|fo%%LMAFbeqE<+32ne4%)uMe62_b>HpPEns{%YUMB7+j?X>y-q;4Nl%r3xVE`#UW#+JYTW znPaB{29YWtARw;{yAZeKA`T3MM-FWlEP+&scIGw9&?+Q+EK&7idGrPIFq*>UsZlDB)NP8S-PSJE?Hwa-wLwKP)#v`Fwjcffo=b+GgQ>4e?lW zNz(sPrvKw?N5d)gMPBb_3puhAeuSUv^ua|x!A2hc`4yqB2$hQ@69WW0ndZp>!jj7j z{3hv(&19eS3H;Wj+vAJ}!u5xiC#ciAO9%^j#6&}YG0WYy!RA!14(ispsfu>wxKHxg zH44VBn{kd+5{j7|3OC>SB<`&Bw|}yWmkl)RP}3%Bl-%hha17rbIl$_EbS(lQF40nO z`%POyxZ{1TT5&KjFpp6pF*p<`*0GWTEgmhull5>dQy zA01&&KbMCZiLo@Rw8v0ciRHR#ap2v8~h@_t$-lP>q_yd-sw21!`hV*ANcvm zPYIh}j2mgh@3I+-!CfErcb@{T@4KlIC7euFGC+t?^N4qAG$x_;1zf6B=2$h(Sozt+ zg@t1e8`(pG^rm1023~BD2ERV@Ve^u?+^avf8O(SJ>W+lRr>niEkFAAybA&6^4%$!M z^*T7V>hPr>8WV#IPpaZ(*MYz!y4^($s!zZ8<~H2eOPa|cuS0ibTYl;Ea8vb1E5~2I zXFIbRKb*;cv@RT3%zxi~?ebRzC&+|woq&2CeT&EmiGoYbQ}*;v#%!%gGlOA|U9{fs z?55@QTNiFlXB=g(B_I8!NGxbgv_g2aU25K$K5fl>tf?+j#13qh-X#~6x$h^26G^eA zPU`Nr8^1~Z2%Z*mkmvHI-x<3^d|th9_h-48z142ZL|Lstzw z@YZ)_z%ohvF1P3S!|=*w@mbHnh={!CNSfNcp%*;c5zeF`NUiAVWl^ruzh!I+#v|5g zR{bnH+Xcg1mFqG@mHCzE{iyn`N|@~ZPvZ333<@3hRXy~=iN|FamNklR)Lbk!g%KPp z72&eL`i02|urBeZ)Wdl+N5I(%@sui9jru*?V{Zd0!GsU{Z;X!hb5;tLLPu)U9~wUZ z#Lc3-BQnvjw~VcjuUSDcBiwN1?B0)Fax&Q&`S*gmh7v2q#hL!c3C;yQ4R@;jelM_v zV^gCe-6iya(_>+NfxFs0|FqjLOjdGp;xwy?5uG(`vgYW}Ap!V9qImy&z|q$@w9kmT zOy4ck0%N#+N6la>ai$?rU^rmUZC{{$#)ZpXKeQB=Qn((R5Z%l%6?dvY9)naOwCTHu zfMRH?+XQ4*-tBGJFR6Vnt#WITuvIDdu->GtA6#F0PCI- z!-E!!jPZJrOrRZg6j72G=dGb;=vD>rPbviUfcp1F!2{{pfn4 zQTMI8AyR5xX7k_R>g{ahAJb3XEHxg5^=RBjl>y5#)0V&*rBPD_Osji zuzPxLMLpbpg)r2e-M6`7T6>aZCEpDa8tO}t8Yih!_^SEgF5?s58)hKYbswM)mNRbk zqA4H##bM}rMWqF|=&reyDV65=24p@g%VM-7f%hFf?4r4?vAH$fHfwbf-Bk4!-e{wM zICobvAzfxax0l9O9s6;(+Zv5qX(bIVl$6t_%~x%TaT}hz(!|;Kaqf@K$tj9YE~Cr5 zUS@al@?mez5M+HOonunQtCryBFlQNVpLmca_d00mD=Cz3)3mx+I&}1=kzJu=b|*?hW4wuS|rm|$9RxrZ{N_| z3&T?Ciy=gEYi>+;O%FdU|Cxt@e;oK|DQ89c=i$o)-wH~aL$TFaF1@o`R{4uMVFh=l z=c10jn5+$7!YPa?Y`QC31jz}ihKKnE{VFc%IteleR#P;)G(It~p*QDxqc*lNnnj*m z_U>TYwN;m?(My_yRpg-Tpk)y4bQl=u(P7nsXS(4#F2`Xr!US^R8G(34Ws7Or=_1J% ziCdHiFU)RYRZ{K^uF~DIOSrF;sODaUR*IEDO%FTT@dfUTJtH_z=|b6(0C_g{$#DNX z1_A%ET(pcD=$^M|n45)?81ek)81R)5^;xX;+i|44R&qiVzBgyC1gZCqXGaIa$+#Ed z#(#=hYW9V`B(INTeoh*5>4^0SL*J7w&t^N$w){6kj89szqld zbS@wDq6~oF=O%MeUi6Z-RI}*M#NqE0YhFWr{@Y6|yG`L72r7mj_u}G3V+XB%6}1}f zJw3o=5vRDkqC_-jSZHvZmwoJM%N`EgS3(ggrgWnBV<_z`GsyC1_>8DV&7POug-3HI z&f8Irn0Leo!Oa|&xq<>43*Bm^!x=?Am?VSNEm|BSPK!8TB)ol&+nKTvZW&Cwn2VV~ zIGD1<<#wcGS|L5{fbByhYm?g}smIUi#8I5KWU`l^-;$>7!ltKAO|H3 zpz#$|$@oFy#-J&;{FR<|Chg3Gb#Fr5aiADsLR5~KPQLVOSZw6dd9wQ$rAOn&w z*R5w?vP$b;)#1g#a#r3F%%L(Ah=th!EVvif zO?Hw}VDBn)t9JO_&M(1LoD8T`N_cV4(Vi0r?vT_qS^*^GqdxeC#D8T0eX9kKqSX_w zcZ_EtrHEV&Q7XQ@x~O|?i>Odu7_=l(%6G;#U7&R!fU{mILN?QzqDI5#E@aH&x0G%` z1&CmkT#Z=Z7XADiTVvV2#HoTQBm)JiW;C$zB=EXz@rA0C9|Q9ktH3FJKM;u+X3PVw z;}?5%1L<*bj!62!YnY`>PfmaOU88+&h7GGt>^2inuC!>MW^-4mgX-H&TD58*P;~yr zbRy|#?>0XizvI153JlLM#g3FhAJB(A$xWA&k<8X1bhOqE#^r$jFzb-U8x86=LM&_P z!1x`zoXiATjeKxsnP?iIZ`{&?S5|muVBmkO!PxD;jo4C3I*SATeI|q_?RZ!(GoVcy z(B|+2xYEqe56Q?7r03j`rBh?oFC!;Q;kBr;I2SP8<;4C24IwUbNMf>Meu?xE2q#03 z)zt%c7gw)$_0qN2upw8PCP9Tl>OTR}Iz-C};>#ypUHqLIG+Mqb9ffT5(8~-y_wz^w z2eHb;k*YXR= za*f$`F=W6yUzyu%OE5T?#c#@vJo!jTs71L|0bHVv+#d0%Rb9m?+_N$|9#NtL9ts*R zy)L`Sh=1<)7Ht^E0jgZM$Sz6duW?*LGha0hj|Le6M&gDf3PDNDCb}m++-PiGHDc?_ zSj?NiJj!B%%c(u@1SxStL7It4Ks!ob0inja^Tq08Kx)vu9W2Gm@35_XRMWZir_Z+N zlB(2x8M@>QNV*iDL)fPAIgQIp2X&3u5(X!k5wWm}QLB!{@g6Zs-zbpBH#xJ5A&GF4 zFkZ{G?cfg(3mfQF(rxUb8x5NTwFThI%ma8`+j1a1aWUer&(CQY#+m7A-}^k(ytT3s zdp8n#WGCJzKP;#K1h+OE_MR@@3kp4Iqm!+BK}eO*aUI#;7YPVz2^cW|Nrk;y-<^Rt zH2I3_{PKyM^qaMf$l8OrZEr%&XF2{g7f>BkoU%;L|M3a~r+R@7e>JPJRj+LK3oBE7YNJghP|Vvdg@e|Efmw zEby#pzxV0b10hvvA{-8Cm7w4gUl<1bZ}BzHHzz|5X@C)KJPGcR^{0g1;coK#+zUoN z3oC>vy1&Nhb;9+Kg zC{vOok|PoS?bV9=fY5^WhpHIM&_!H|Ao4>!PMVeC+W$6*?{)5vpMtpY#+m2YjxSSm zzD;zv9=Eu5_h+49lxX;q(0IeCF6U8u;;7GC3fc#frM_Lj;7yFT9=!yivrep7)TKi) z$)B$S3RpS$(~q{JB5i}-!B^O?(a_q0qUi;>G@{*Pf8I^!Us?w{Uw)#cuDN&bo(G;c z7PpXKAk!CBvOI*&6riBZDAUf0%70 zO+T30m4}nnQ`A%sfa4AWlMEL7v9*~JR9YuFqi*i89heh1C?EE^@jz}^X5y?Cna$z+ zR~*sIX+?DcHuhP_1uY%Naj?fou4#tVo~UOEBRzixRa@isPLxICgNtI|yuf;biQc8m{n7_A6V-BdGC!hjeXsqaRaXW&geP}~tdV3R6d?OGdl z3AaFC61E-Ox> zssE_WEp1lBWo4>i`+HkAZgG|AD}CEVsmEf>*UWfArgdBGWN+`c(;t6*SQp5+QWCh8 z$&tBD>FLhA#{Hy1Eio1ZcI=j0hYw^33FxoAaz{;V?(_U&jXbg3)m+A|xH*c_(?e*8 zqS42hKFX|dXP2D97s?M`eg2#o1fW5Xmo+0R@u~gW z|K?pE{&eOuD4)IZu#wx@A(PedFe0W@59@1kv}wm5^!^MyRaQtCtbPGOdQQL~NXeyX z0fPV*pr4chPMn%50{Zz(n`+GZKWm{Kweal-tFMqyd##@X!aS-)J$WS4(RdYPZr$Wm zCO+Rcq)A1Fr)vHY{3*^5y;ki2KDT4GKYd~)tiAMcGqkmUVtq!Y3T zj#5w@$kTg&Hd8?%A?N&KJ{|~j9W15<7W?AU2h@dE`Asqe{RF)ZPvXs5KwR&6%~&Ak z=q#{C_96P97Vy|aJH((YSFu)@7SBcHo717I1l?^fICI^&T*23n@70%!AR7pS?oB+_ z5$x}lE~5SZ==_4*nO%?;f`)&m2-Kn?oIk8~Vq#dReJ|!6SehA9K@7X>b)yd?MQmb1 zPa;8TUjS-y`Q;oi=(D4i_?b@uAvXvGuZkW1=e)M-UohInD61maSRspr*s>>;C_ej* z<1&ouD~eI(Gtk{tmDe82i$l$GqNm%TE2_=J&a+M^dwcL(%P7$KL&6mBuB7@!rB;zx zi7_nLb**FoL}dv+VBVpDMCJ4cwS5e~ap|0jALoa6)5((nGfy_dMSfS>ogfPw{}p3B z5+ceq0PaZufcwc{k>8r6`gj+@e2`4dQI!N|BHLMv!N1%$Ol9fQ@ypRGeyT7vZ@Kx)R0ilMS_$2n1{|TAwD+8x9PY(EFo4`>ZA%hE8yV!#B=^Mj> z=xTZK%gW9r+^iL;kUKa7+@*kOJa%dwBfRR!L!P$n`7|q^_6<}KPj(i zOX}7zpjk~n>Jht9s8Sy)2#?a#yd(GIy`^stLjEXcWec?%D8J5DZX_O&Pz z&y=H@la}w{{nest{M|V=4wl8Qxq9;N2$=|1vJt5W4vm^cvz=qzv$|Pei59)Kl++~4 z+NVLz!Vh{q?WJ1 zXhD;Pob%n0m>QJ&G#7)~^-{8p^!y(khIK5v`Ehes=9QC!XX`6JWgLH49B2GCCn(bJ zC*w6$Hlt>clieUU;2z_IbNtktmM~6Raz_oycDG%gd{nA8T1+^M{FVlqkFr6!qas~# zGWJLz2BTVxTltE+bGN|p;a~Lq%~5Vnl}oiKR?8<3-Flt!v@Wr!$U5ovWtS`_Ln^$@ zI_b@3*j`GM`XoO|0X2Cos$2VN)6++M4mMX?K9I{gs2UtG| zaNb<>a1q!squ|_lplFCnPzGa9e;Bo6j2gM`d86l{>5lQ~mT}7sOWI-GNvui~3dL&i z@lC6H00mcdz+oj`=RTlnd$*;JjJ@1C68WQHXQ*X#xhJc0niv>37cn@{kh8~(NS0>P z#?^?V_8=N{qQ(eq)qAD>TFxKKeH?DQe&#a zzR6%EOQ$*m%YAj5Lbq206tZ`dFkU6Gf(zuFvJS-~yo-Fv!P~wLbF*v9ya`U{6OiWu za4~LdE!RL@hj*5~ld^<)#eeY)Hx^zd4>?AKkMEc>7P$(P~QbM{HnR(*>fOC zXg2jqZE%Y4sY;S@*IsD^oq%`)29T7qze^mxy7>EsQt8^X7Lnpo!8KT4NTQ^5c=zJv zy)JQ)QdnlUmD)=Tycr2mqJ$8r^8B=8d}`J!!l!?wtK}!-Y<41YY1&j!n9P}a!>Crn zlxs>7Xbag>Ck?%Aj#XU0l$0NsdhtB|FZw+Vs#|Mxr78sJOz&4(KnC6LQs{8Zp+vPO zEA(-sWT8Kgf!-sshD=byzVU2|l{yY9vTXC=%|TlHi+E(Q!CJ+B_tBStz60ui-O9B$>4;&Gh!b^4p|NpI6vln+ zv2?v9_l??+O`kNFthJ@%oz+F^ZfcbK=`2=4^Ecx0kN!}ws~bPuvvn-I|-a8INaCU-*ktL zSS&TY5y9>+$PJ|wGZIycyYqH!J$7ei%zvi;n2-e)XmL)N&Gp`ub3vzl-D}CixZbB~ zlUUGi1OIHZF-7w(`4)`bB=*cZR?yCH&hqvb$((F>OMts3Gndoh2CU3n?7@zc%uPLN zo&=S99WN^^PWbC~f<{Z=;<^YAD+sHgqh;_{t5bzvRX#J|&ajk7&IzaihB}DQrXTeQDNYkx zcEWd*n~V*p!>|UmZ-Y+kbb^3Q$nng2@y0S8T8^51SKg%7dn6r({dK6qx!1zH%UvI% za%p^RtF=|>4FdCMDaH-(z^Trx{mb8WfqtnkI1m&ko+v-Ar1a0zC&gTUQ_;OK`F?}5 z&Lq?C9cs_{Ph73c0pl(P$(9s#)Q1R&@FIyd3vvv@O%)5k6_L_6GlS$jR`IqV(L7E5 zp@MHWU5ha|t9jCjapP)HNMif((UWu}8DMs7ADc+}ZD=-r+r-uSaktJQ%n@Cm8f2GN zJix&U5;$yGfjIQ{s|zH3-sP0uF%dQw7Q}-891VROPXjs9R6olVc*9MkAtS~{(k7<0 zyM|n!eesc$>Cx4i_{#Av!hVPUv{>=S;nfS%m+TmEf;UB;1 zUIpcOtQ@qF%i3?YI)3mOt%c=hceSb?=mauV=AVx}kQ(ils2X9wAK02UT7$}$-xk{4 zlJv~r=7(T6wzK%a4`&|V7&~v;M@oO@=Tf* zYQA#usjP8ltl8|wW`V|J?#rkl;?r$gVJ7U-Q>VseXIXgmu$6|IJa1RX!U-dvhC zTv|Da1dhoy;hgjfyWPyHFoskPoPp8e5G&bPa?;V$#M_HxLrCdIvgH7LottA z7L1*0ovZ%*`|^+e&yM#^RITo2B;OeQPB@Y-9Z!;r#lQ#uSWO}V$4Rr|xhD#zU&gph z;D5>6t&%)q#iy9?jwti8S^xwpBfg62o+npKeQNy^;c{%5Y}_*ODX#VFd%?B)|3*L? z4X}}OWZs8gEbfke73#Xpp@JGN^_-oNpWdE4+7eH;K*SjZEBBGIpR{F73$39?_ez~k zS1Dun&N_XkQ>q-*Uo{pot2_Qu!V$aVH|8(1x9X^#mH;A%946L^wF+`OuTKMwjmbiS z8?JusPIc=%MVoEHr|F3lPXZgg@GVxh7b*7l9ZqBeE2Dx$6%d#JNqN8U@0K2)dLwF) z7w^P17j#!YpObd^K#5wMcb6;I8uHNP8leI~)OEgv?xn~#dLaRSZIO6Z^xsIoJ_0#} zS%itG2z;Zf<5^o94(E@=Zrxg9hl0;VV&6GEdsv;=9@y%??lN$iNQ}Nb=}4BIjSSlPl`a?^dVj!9a9l&Z0gD~_kky@V} z!m3}v*WktR&nyX9Xb8$d;;lA&&1^|MD27wP$Osn_DDf$=qpk4_o?Wpp!fEepEohR7 zhLyf{_S*i@YW=YI+^11H96tno498ZCF8a%i^NSY!q8nWs=qH7^lY@u9du+3(8_^sATckd{pM%i4%^IIfKN%BTWjFE z=eW#cT@X}a)`BHpa=nk)f8`*`&xTK|$_E`n5cE3?J9^|M9K>lz*S4T$+wpCVYvLOa{?CMRRXK-nj%17_XF9T{K;@MO_JMY2` z@~8H9603@6{VM7~7*2xpbmu{!3`Qs5oS?&qOlF09@wX>%B1j?mrtDikx|qOX3|sbz zZ2$c^64Vq!(6A$yIb+)hP6yy4Qea(dxnzEN->NVN#|zYyG3)Za`bj+K;N)>%B@{ZE z{d?L2bFHQHw$lqex19Rs5;1IcZCbTOclD!#R41{ikyvFL_B6Ty7?jGgAvJ2dFHDj# z`);tmRc4|4a*LY>Kq8fKtM=Un=N~ro`(A-i0I6@$P0}>1&S!Z_+q?WATD_lK778|q zsMDw%-G1Tbt*C6DSix$}2&RrPZ}3OEv6T)8D3aDY=mG^-Woq20SpJ!m;Vny_2`YIcnRQ-)&5&;WLn*tc0bi1%CBfqlBuv$ z{z7IyrB_Akc6C9Y4gh$T>)9^!vlf;%x>r?s>Pc1|B^1Qj_zQRjv}eaoE~1`{n~S&} zdZ)++*f#Zgo!VqdcYI>I+He5}8@yHI32N^O-E`(V&2zsnpvzcdLGs4^PQDS?DBqTb5}aIS_j&>&J+fA1$R`QIma)(n9ZmYtshUm(r>I+m$v!W0h8Umke#diT)tF>vB_+|v&i-S(<&!KI-}`0gy#UxDD=)V#Z4kKlKtbnb ztTZ0Gr9xvm%0oFt6-hGN(5U+%W-~spY)6(PG$vAifVCBr3%zLEk7r*Wi`tD`q~)Tl zqGvO8&}F~6S3LeVb-SL#8aVM0?`eGH4;3fAr9f53I9r+mPt<(|?CQhFSwm@$zX6T% zgrc%5I2axMvty3JvyzYcaNHwR$E8)Zn-|2#Srx}z^6D?xRUS#5j9TVVyxKZG+6mM+ z3+{>BvMhNWAW_vy#|C{)aApQiR4hwQF9!JKSIEPlK5rn<6`fk?ppSk;6|?F@__A>x zDtp8Jb5fr|0g-=u(3#FdoP-k+z0*-)2&7YwVdbwv3D=s&N6-;TQXZ#h*uk_Lyd7D) zER+T0kVP04>>e)T_Z#Hr*%c?hrN{$UK?=fLHRS=R1r??>sqv6>7fwhke1suD8{lVA zW-&b7X;FD|tOtVb@+|yG0_sH1x=~3X%_$9W^DTt~%^5K-LthRtm75ry1A}fX{L?uv zgm=2oY!8V56r9-Jha`rd0`i1eVJYv)Ih(uukQUpUokYoE+GXO63Q{w&I^0CM0Ud;K z;Sm{6djvYgn9(VjLL?ctydimKr72>rEEnWBx@Suo^VP^?l*C~WPlC?7YShN>NfT%58*qj7~KqXx`=t(c(!SW`^vb3FU5QW>ZpJ3b{D6fh*nu`Ypcd(GSR zNg)P2Xpe0A@00s^Fq_i#UMe4edL*doA$-I~ZQ;G=Wgz(urM}QqI2P#T*NSLV060`&J*#CR*-fB-=s`unrvcwBGG9jW~{LQORRps8UCTA{fY`bngGKl3ekia*;X z4o>Oo&t8U!?6t-SG4}Dl<8T^xR+@lfPY4q1=k=Up~nd_D$VC* zb!bo(gjqJw5Bxb+7KnwtQ^#s)*%LD))K{5k2gY=QF+Z8`fB{kKOj5*cj???mRdGNK z_3@VngvjFAX_O@I@hrT11BX7xt%Hl6_8uJ-SEQgm34>pc*PKe_hkO;mYG(|7-Ra9F z0QX|Aixe80Q9DWff}^$(^LSmLn8AaFieOE6cnv-u&neD%JvfkBU1O%?jGP5(<`Cjr9fZ2DPVf&8LP1omMtU&AiA z9{}~qv&AIZ-9{!4^k+XHHf&?EdX`|Ng|s<}stGzH8ZgMd3^)qIx)eI^vsI0Hh!W89 z?Y*G*;{3}CoU&~il7Ok)Xw2*DoU)x5G#t7fMew<(shUv!vzrt;*XA$dprD%Ij%ERD zq7dauctl7$BWeq>Xwaa^TEZ|RKA#D!LM&kMSGXb}E!WaT*k;i+V(YN~6%xpkMd?Nz z@H6zNS30b&#}wEvv%Go8`?uk;Kpc+1T46g z5QyCBxdJ*7*NB%2r79?CAT2IK{BV&lQXMq5&l9m7Yz;rUA+*qcUCK8$lmKYs#HPWq zC>v1LIRu^VtBa8>VE->(t<-ptm3m)ILHBI!_k7UT4PB{w+X%K?-HuCErELmKoCCPr zpq+f2$iW`nkdCd;Qjz z_j*Vn0b5s@5pa#Bg~;y$2EX}Mt!}>DU_YbSa7!a+Cg`$pkgE(2!YeDgy=g5S4kcY> zHMxa`V>5JuG3^I9K#EPxTW9(q6Mbrsau(SEC|-N$2J*F?7MnmC$jY4+hZmRJ+*qwG zwQ5mN)6M!+B982_0lPOv!PPp*`tdj!JB(A7A`6fd8|uttaR+P(gTXi%q;jQF{e|$< zdz~hG9L}Aah_1@jS|3gxgvx!@gis>JDhh0fw!7fVH8}*tZr~JnTWI0y0S;I0N*o=* zY>Fkw%%tlA-0nuYAR*Cw+#DbfFkrhP0eNTzP_&S*_0ac2?5*OJb;ICKYB@_y)&lR_(b?}U%#~`5$v8D zUr;*B7PqXF??eKCTK3v-XZUj>{K=;TWZfrk@E{bRVS)vG_5cE5M%?mZvmVkZ?6qKw zYP~0r#tsR|+X*#3U25R&;SGbebZ~C;nhGM{}0$mRiV#2_j zN}FVXrh;3-fT!|@BiCT(fC8p_fTbR_S_$@v?)A?LgQP7JVRj!#WU)m#i~VIz*~(R- zc?xMYAa-8^eY2{k^jVx$2nD+@P26pw_yEef^Rn9wOo6Fy``b|CLu{vpLEQ`>^90=H z9Ogb`gv+O|K#2S9mJP3UmX0g3nRgox8q z0KhV|HA+i56<$f{t|_xyL%b?b(JQwrzn6UUFvXF}Kh?CJkU9DVO%MYfYtzB%IuGNc zji*Tbhh*4DRJ@rV*rgW?`-9(pJl zyvQS>E+&b0)jZMOOE{g!wMnpQy+ZLp6j|BBXZCtRufGd5G+*4(CYh9x@3ZbrwA+-q z`yhy=d#PdfTU+VJQkp`^;-BBE{dp0-_d6llW~-~~ePDf6+QY-IyKGLc`xyG;0{G?f zN*i}hR3Z^73-1%*w1D}uK(?1(Sf+}#cb&&BMw1(xftpmrvuo6~s3xsgHiw3iGxC3| z__|O3ECpRyxMAY)#9ATU5HIg~S6HbUTw)_?(#5L=14oB4GXaN%k&_2icWIYCOI=dB znMc-CMmy;QEUm(q_c3g+1TVjxvEsGXs;RDj)xXf=hvtACS`-!!ks^}DKyLiS*hKce zG2tnD&j(yRJZyYnS(hL{aR{t4@ysAj`Sdz^{3_+D6yJR7WpYp#=$|9ED9_jSVKcKi%cN#p4HgQ|`W z*MRVDhTbWoq!HDT^6ehfCt+_K?a7Lx>qBFvb7Q#of7Nk0lhqYOu(oY-Rz`MG4h5w| zHoVrnUCVfq+IM_dxGdsCHV%qbOQfyu&p_EZ*~9X{s&)}CTQwbgix??4e3X97kI-{9 zsY?d+&0pzm`j;3JGj8KMRJLN0g;oQFd#=_yPMOsOID`L^V00Fo%#(^4^*a`^R(EGL z8v=7G3mqD27}su(P%Kx!HQZvCsx*Yi|f#Ds2jjB(Sv^ zC1;ur6sHL9E;&k?Uo}9Pm>eglMAQ|}^|F<&{j|#})i51CsEHP0uf4xX?Y9nY@QXf` z@}(31Qs%`gHqTSssE4Qv+H)mXx!(FAXVAl_~U(IdUxcvjLJ;m{Wl`bW@Mh{ddt);t^({*z^+Rx zO@+%ae7klWakRd%9(T)a(*|@A23WTaj!fMCy)$OsuRr35<>Q0EKV~T90lKhGi(=fm zQ2iO@vxu3)-UeMeuMqxHesTNV$u;+$?FPRb=y!|<3;QW%1WRJy((I3Q`tI!nq2!jE z@+yXX?yCVmBHm1CL>e&$f&h9m4<9mYf6UG?Xbj?bE}{%p@^A;-S2-K(evo$N`h^8` z8dTp}B3q$-+o!|H+#nV_=oj&qdvp@8$9B3jrh?H>_t>*_{A@~gX!Xx7<^i5>#dG?K z)+Dmc&TrzkwwF$&o7*EbbpBUcUjk0$_QidUIYZ{D!Ei`~B!tZKl#;|TWR@~SNjStQ zL#Cp+%oHgzh0H^S2q|MSkD1FcW%|~8)V=rrec$uk=e?bC-gob{*ZQsBT6^uiUwbgx zT1nf9JAOC$tY5LMz-oPmj?+D4Y;~VoD002HXvOMUJ`a}NaDGwyGSFnMO~a{HmT$Aq zsGPA2R`3r$LUXVmEdPC{%#@cN=078viX}ROTjQR*Vz)dPZ8pSqeirq*G3_z_xwJ2S z@|9CQTD~-2N<=9zV1?lo*Fq`2rqqM zTZ&DIHF<}1A4k@cX`DMgP23jTiP<7_&pfVtI;GurBl*TrVKj)(=Q~+FWNY~q@#M#C zZsE}kG}#<)Os#UVGTlVt&MnJEy0VM38Va)^$Np|~g5RF{ZzN+J33)ae9j`3=H7z0B@_Oqz&Q;Z{RW4zm{!MuuGw(^OeC+25AsluCF z8m_<^F=avCLeE#LOvy0~s$4W~sdgaZ=|G0$>{cg%{ZqGIAL}7VTIOvQL< zu=MLUZ(EA++*!{a2`jP7u;+yeH~TV!%>7s!m#>H~oas^J%G%@m(|~W{#etokUwknNUzZrfX8T4(!z)Y{J#{SwynN8@}By{-FeueEpdH* z;~3A$if%IrqOQDW}&olv*?Rla60pcM69 z>HNyqLm}}OL+_COj7~Zw{&F3TL9tezBx=4nNw8ZmZO(-G=j)N0wJJ zZKceRgNCD)7Y^Y!B5W=@h*cTZ$qUC!!ZuvX>9(`9u8KZmXM-sb;PBjR3s?^`WRD5u z^?I5ekiJMOs%j}RaZbQv`G}o39HxSdmM%A&Y+SH3*3pvd#Y%mrd1`C-*UpTPmM^er z4uW>aw-lyFFJ`Y;x`gk2u>vIZdSm9tG#r~Xsv7Wl=fx}Wqk_f3YYEvYx-l-!CLXj@ zSJdPinv`KuB8&NobuD&4j$zX1D% zC0z>m9d0c77rWr-M%n3^o6LeGN^r=Ly(zfKA=k@tZuwEtfr5CoRL`ZOnFDW|1X4qz zO(txW+Mbq)?m^wQjP-=g-lN-1`);sF4=BO!hs?fv=NhzXPQQ}7lMlN*jF5MRs6;6s z`ICjHfB)HgKf^Tm?`-FX?iqBlS=-L@;O8RY{{thh~(`>;(6 z&HX!NAL_)D#6qYWqKA*mJFdYx0TnK?Ka;7W>o>f4=L~1PSjck)4?>=yzgUKgVQRPk zh1NE!PoABfz(5=iVGq6p?0`_U)M+Z=y(?=Namdri`ml+cznPhdTjA0j>b*Qu;@LCM z+&9@naAM((kX90VIA z8tN;l^fKSB0|3JHk2l3+u=oFZCBS9u4oxv0SIVhiL-uBY5fx(Zlwl9LfB)3Nr{R!Q z^*RII3L%cE8}M1N$jFIjv-u&|%yE05y`f>-_mM0&^iJeXc%l)T77ZULA`qdC!f(Ih`4OCp!~FS#=G&a$EA*%JC{6zPCS9 zJ9!0O6$4@q;ogwFgZDf!)fH0jDEWHP^G{wK+~dt3IGBbqrD!8JqMINIdnAB8i6RxQ z!ireAvxin>>^YQmF5|j}R^d1qHDQ=m@d*oky8CpdhI`nBu@(wLW&YCY#+Mx4H{Re+ zGCz1H9K)hyzm&#az(Pe*T2SzHtD=8l$;kDVq0ZV`3r*{0Vd>66^By+#!UmQ2RII1} z#H`wF3Il=+@7EI&+hq<^dMDcidJEUxZfmy3`uzG7c=QaV0Rj7@bSdAfr?4f#+xW2G zP~=UW5?+xpOsBz<2;*hrxd&7zjU3s`iE-yiMKx^bP_M^NlO@X=Zd{x?dA~Pj*R{y$ zlBV+bCzpOf`>O1Tp#h2usj+S}F)n3~hW0ZX6HqJ)Plo2pX6e+UbEh=6t2hf?E9^C* zB7+=P#$&H0_8uGWlA=L{b2T_hiCLMjbJUDfykyhAij z75iH|@hT#mH>>={?|PBz7aF?A`PZ+soSzOu84#Mr{VXdeFxoRq_lsO9y6J3rJ@VRq zn6{IGac+b9*+Oz%LV2@#xR2QdD2K4vCVrLMnOdOYRBj)oGG6*6#h`+l5Olhyp zyYhzckf_!9wtk^H5zO)2zSi&E+@;@S6TAWAy7ZyN$4HCf^F>}~7I#btD49qb5q!#1 z@eH5!e5m;W@S?7HS$5IdpiwZ$1Q=wqlcid*8a$H%1<8_*q+KBiw+z`;(1iCUdS{vx+>C7!#JT}k)TTv&HvEDR zNt$>?4qhd3r!tpDYlul8mNOurv_F{ogQ&r9!$m4Rx_-Y6X;r*k_|27$cl@9wX497D zZe_A%kI}4RjfIikJ3*(e+Map1)#=NPVAA;jh-nWsTku-$$@#KOI?QI=(+@F}CL#tg zOHo_*7=EqK4t==}J>Wa#HKM4p2XWHVZ3|zn>w3($pS!WM9#ju zF&D-WGjopKUzLibmRbrGK0y5*&_~MH(HFb0qt%f(7|wMlY)d{ih$d-D>TYM5d@!%b ze#YzK=>-8FZO8{08n8ZC_~27~di>AWS#)`o?Noc z$rZ-I_~NlG=Ln%wi8ltLEC^f-$Lor{SB3$(&0o+ggdN+Z^?g@O7&E8N-tHO9rRqzkvjb>_ zBl^6+sA6fGgRziz)`;4y+YYJAxKGK4PMYcrhLOAOt4W#a7mahxf1@>T8&F$Qs1Krf zBc8-loiiLc(4=HtZ>8g1|Avc`W-vWeLBY5^B887cxjlnD!^bCFA!r=`I#!rXv!p?B z|8+R4f{>Npi<+($!2@U*e+)8y5KnmJ&Z$X4qiMLC5f&k;@QHb_rISVF@@cS#tirXG zRF5e2?Tyv!Xn1M_AoF#lcyK_M;M`lnSuO1LVJB(bVp3e?VZ39T64O?9=+<2}82w3i z7`;%AQSQiom2g)(iu`DYcDdC{1S`HeX|tF&a&ap!0lS?$%{~1-KHp;DMWwuMHXO~) z_nQYEI&!(h2h2Xo{n+$0a|-kk{|wPbihcXHHL(*J$)x?d$FsNLg1w)BH*Ph z3{Psxu8+EE(61zZ9lX2kT;?K04>2na*rhDhMN2VwArV3dc#tkf%CS|le!wmVkzodoOp zeKrExuJjb{f%ryrlF%fJ*M)BWKE{L^Q^E9yIbLV9phevTXI5z90n!Ao2;Jhyj9c2e z!B+6snlwc>-S~!;1ze=)slKexK>YooN=^7C- zr_WNpHeNcxv{jCPH+;z_oJ-1Ut4nSpgI-6`9v*L!eyN@T--7lfLHje`iU$*nHhrhO zIlufKZu>&(uqOj|Cf>(jh@U;%a4qANQv6sqwbx|u>q7pW@52Z|>3wjrOrVfjQ&^)= zv9;N__~r-xwW3RHe8ung4G+J^vX?!Z+JD$D@)l4ydn~XeD-x)aeN;dC%C--MMmTbR z8&suy9aNAUkZ`Hby@%ppp&H~3kDHKIF_*FzcCL%{=CD%OzBIdV%gt)@R7s`QsKPB$ zL7aw!>$!G5iQ!Xc-(;}WC`TH7rN3MVj*?#)9;b?Jkq9COwIZ3>`8&KK!|OkMgx)N@ z2;w%Z{MFmw$X&(FcCtO_{OF?$A6?7wxOP&O^2RQW=tGW6Jxb=+x4KF{`j=Pwe~XqM zS#Om{)G5@EiSq#XUE2;O5yXXadG+pJ?IRUucqwm37<*2sKg=JV>#(FZ$)&S0v$eP^ z{ZKy}y4Sl>G1q!&b5qdAPjLB7f@oZ(*|cb^G2&GMSay{s*&S1>Uw&RQ5?datT99Az zO>@sbF6Q^s<;&zpCrj6!l0AYr@lOk_9$BjXCuWWXX|!iN&3&`_tHtW@#&Qiyt9t_V z>FJCO;ScnO;PXp;lOr7=NT_7j5%ifzopj_OnIHExuRQg8CV0tcSnZ_9%xeky!tLWf zpPxAdKIMGdGfMdet<7*nO*zg+&rEk%tz-!&!spU?fRLd5+qt4}BS9EJaDK}|Yv~wc z-||5s=+FC=BvMial&7g@;kHyeU!Az zq(uF&0mD)LgYIl>EAbJDy_IkHQr$zO;v7c<5+R<5P!IWtT%}r>8*J^E97(jaoN`7_U}r8%5zK*jgHJ%{+jK;Q8jOV zIxNMmIP1Ukc)C?c$XD0WNonmzf@mx%JRwCZtJ8-{&zeC`0MM{FT-21mx|}^4r@si3 ziX);v-EQU1%@-|+A(I*Dk^*CWzUIzg_YsTnwI#8*8*P@uHBECnzh4wQeq5G|+mh}M z7!HPoF=5<{<^1d{goXPY*i^U8vB}2?=G3dR<%@OG;i#XRonI5P#}dlqf*(xUUPtXa zM*y>ks$sG37kP`PS);__eP4RMTUDRgw0l*?Lr#^dklEosUJzySdxOgSVeKPR)TLAH z&Jb2i0%`rDTBjTY{W!9Jm02!=Cz*$1d5^(%jlCYFlP<0n|lpLCWT z;H*(Cq5r7#y5Y_kP?XcwXn>rfuwzT`#?S3-knh)$H)V~_xz%w9q4y)=LUMabp1(g9 z=WvstDcUqpZQPn`X78EfUvW~{Yk2uJE@LA8P}?FaYM&{hsVo)EN|RbFHG{EwMbUEe zw@v>Vr}n`e_CT{~46Vf;bLX9WP4dleV_9AGvk@2>OCTIQ>?)Jvq?v#kR`Ar?XNy-6 zX>AHPr+{U_$ho2tllWtTlPlLd?o2On>?#uIC@@KYir1#EsfDFI z8S7U9er)n(aYwG2iB)W;u?yYLjrkq#=&hAn-jiozD1wqKpUU#}DUfJfT%C=yyj2ta z@!h_J0BSX5RrH?8x`a0$m7@)Vjvp-ixLq5d(Ea<%do8Ec4Vps>&QhHFK1_SwIk#}A zs)_Zz=huFpa7$I=yEI3AReDaEN)MM|Wu)1frd9(=ij-s@jnfR6D6Ot-qvA>uf{lLd zt0r>)Pu|B3x)n`zjMB4-3ZMcm->FkWU~9*M^bB6#h=xb+$_tKF@y1|0p)K^ z_o!h{aw<9xuWq(kR$QbKRvN5WdoJRiBR#mSKXXUP*CXKZ8J)GcnN^EPFK18v8XJq` z&oTi{9GtmMhq6I4rUegYV$B=$x|8l)SByJ!C!ak{BSa{ESU7TY@Y$w0pUL`OoefOy#&=@Zf55LH_EOmZFHGHiYUw^)B1Yo+u+|4ubeB|+oOs<-9;D5)Dm9ae z(q+AeXZH{r4Yxc?Ym-a`J~6S~)J|G)qgp zCULzi^81e!gS!E}-1q6dXuS4quLi6I=yI*v&Ilw;^|!!mtLY|HL<+=Mg-bi5+&hEY z({w>@sGtnyil3wAt*}Sj8KJ-^4=+pq6Y9!8YToE$%h!`Vuy=1jzOs=zxaAF^H5AP| ze7eUgBB=^L4s?mFE@&5&beDB+yRBMn1YGqb0eD;mLe%RU_4n1ImDJ(rU@~5f>vs-u zXA{dDdH!Y(#JDm~F`FB@W-Uwl6l`q9-g{f;gXjDn9nSo=-In`W;5H=)qB>()G0Ray;w975 z%BefpUFlx3Jl<-j`cb{df4^m^`!*A5dWxCbZ&Bq&<&X7#pNCJJS+6mLd%5Euj4k$M<2=cNxa6MAyc-CNC3|n(k)Ob*^72~ zw!eOCaAo26TO26K1Ozd?L4m;+tD5ey8dMOxImz8W`0aA)!sz(?_lmokwdxt*Y$Yi8UNPQG@GhR}tjKlpx@kFU*?+T5P5Cs$=NL0WSGk3$kSA3$ z`keGf-JUmVFzJ~OAWvQku37!md&eB+5=qMt<_k6?f`y*v%vTH|)yXWAi{xkW_&hE$ zpjwE=dB7`Tur$J6vA58N0{$YxdV{~CC5hXP$H79s5YeZ}pMx=89xJ}vuhy0N!uOz_ zvlC-BojS^Ak2zP);K{W!zTDfZ(3g8a#^hw1{Ab)|^_lsdrE^}HsD!c8mE9ME=S&s? za(CL2KlFQVtVBOD8Sq}M9b`r&O`WCJ=coafyn8kGkl_y!ykEz*WCcQF?Rcfpn5Ue0 zsze`D%vpk&@zOPBWG(~m0%a-pg;@K90t2u7)L1w+-+B%mjoqy zFBwnMlPK})>L?Z~Bn?PaQQM9axI&UNg$^Hh&{ZyPDjq}*Z7FEv93y3f=Tp3_kd~ZU zyYx|ImEg8aUFDLdmp?E(k>jW*!LPC_x6AE?4E8b)oC`1@>>ZsiE1Tu4fe+yWXS>Qp zOgZ7SJjk|z$d)a+*fvW`jvBB62|2pT`AvIWdKjLNrlp`60$&K@Q?x{B)#3K=ftx@M z5K$viD3MReUF8Q%FJJpOejUa24__}IpD&AZAr>zLaVT{M4w`7Mc4wGw z-*tr}E5NCPoAYI25lZ_*a*1v7L-8grhB&PI3G6pd(Auo0Cs3h68#xtt+ETg?p02H| zf^pERVNpB>1E=xw5tdL{yy60z{a{qut&`)m3~WSIBf?@^Hj3}T)0;jYed(f2h#iOV zimXof38C_YaLS5^o_cp}TFwVjMeIs-@Z;MSKj5@qSNn$vkt|4i{u}6CsMX}fc)1&uzY5&EGe^AK+h1mc7TI3TH_Z~b8NFYf+^H&cIc5=s2uLW_+bKwb> zWB;!Qi00UBXPl#?_BE6ZwLfm`HONST0<0nhMusgAi(0H8m4s5ZoS5*g#6=AMPsjgh z$3a$s5fu&PUS&~Yha7+%r?w9Lq;CkcG9iW_a8`wi?;j(wjS#!SnrV@t`0Zx|3@)@o zWHm-=0)A$G0$XZOwsy%!mYh@gN8C3FeDv9w~F_Th}#m9Hhh!lQL!orBEuf z(%PZFuqc3$>UKfLW-BGeUFb>z#8AWl<>Opp&7J77$$(nc44;fnW;ALb0FNOW^XLi? zk22J;lcq##*30Cvupt#-d7k6sk!GCz|2&o+Fg!jTAf_ zJn#PPAqm5aDcMAm0J)>u;CO6D5OCHz0KK7Tc4t~dvDk>LA0qMSehVcNTKPQ05@TZ` za2W(#uY~fRWR`}?F5Wgg;c?3EQV}qvb#jU{_vp-g8O{TCR-+!!%ewStS_00DF8jBMLtv#cKQUAN z+#K|PLt)BcnD3pi^!K^~hNNX_P0oFQXblPV^lZ{U}9$Lspqu;;LgYN5>w^JJz_10Pn>Q(1 z%h|2E1FS%;APi@r;~}>TwC_3?(I_cvB>5aS{$&<23>@k%Q;UVnhR>$vA~Ih^k`_3u z05K%yNtx(bq2Y^cgdz*y4}9G|#4Yqo*6 z8S>GPO@W;M%10VuALg_6ulYz&(@Ao1xHBa8#*Lc8Ek#Tm$w3&?QV3#B7?$!d4!p+A zYyMCB3I&a)+aAGR153(Jk>0t=6AlT}sU3IYUt^#oBTK>w^{cksW+B-uFXZA9?gJuB z*~`~+qccXZy2U4l#V&FxN)J-#aax?FQI035qYji#wOn9p$a5{ck~4CF5o}~on&NnV z0b6s#lB0NJ8gYmU4Y&DUWgpxFl4-qYEx_{mg1Dv5*Uh%?U*+mcoa(UfV#{HKQehI3 z>pVG%Tgqo|jlIC!qD4Kx-BxrtCcXCnEGt1g?>*_6xZ{6wMJncImXu7ad)Owcxg;DU z{OJ*XV& z411dfX74PU4)=*^7vy~9+&0Z=p&1C70msAFvzNpR^eovi^*Y*}V+FfKv zSW53deZv>NCxAB~BFU=shwlw$Nndp<3 zTiWrSRO067|Ji2V+H*Fz*JC>WI%>LcgW2F_d+C|=*tb`5T2q z$Dn}bps7C>6;B)nl@t=kOUVL94{M$w1y%&q;3RNic>~~dYDAVGNj2#4HJQk|GyDSg z{&Q)pIt1X?>L2b%{eg!J$$;Y_tO|k=;Thx-_~81TulHC`>Eg(0A^j8R*uW8p2k8R2 z|DTIz7sA!AAV5Ro|GhjJ@yDR1Gx?xkA2=$9NHlvJHW2)$i+m^yBkcs6LKB`{2T%JN z?#lSzW~S5R;z~dl2O!@R#O2Bm3G{zDdVmlmd_!#qhbv{L{y&3F=Y>VNMSpTZoB_u< zX>?=tWyb#*h{IGU3OcxRu7M8PVY{wEhO)0wW+SSCsJTCvq!T5zm(Nh6>_2cqU6d3h z2&73=#$U5cfIso@6~dA%7Z`-4{@DMgjniN{9zJ5yh*GvW9q~W5X*36N9eWA9vOvP3 zAX7sQsQ@RE`~1gpXT4+;_=pL%V1%9{y7BB8vi}M}M2QNW45U%_KpYl@VkHVbWm37XiTMl0T|~Z+=~HHP z=@yVfP^qZT&=HZStOaPwbL3rj!;o;jUrtL4N z1>C@}%5`yVOTS5oDu)ZrkG-!Bg5FR>KbePlSMYtR@QX77=Z{6g3>?LSfk;HOA6%|U zGpe)`*DF=(a2VxwyT|ogg%WTQ?El46?s2Fs@CT2$h9lU1^A%#;KpXt4Oq@iy-+}?^ zwP+(pEwa1E;0IXtBEffa!Z@&uqv>eGXvdr}Jb4!wVVZX5YXS>m=Y8gwOF{P$ivpJZ zT!#2cYJYq+Od=_~a-vq9%qR)*R#Il^*RcFTcWl0hRRLK5AgZ6V@iA-pL=SjsIM++Z z8G(;?H(VVFF}2QgpafPJEB*mQC$IpKh7P|Ky{(D(C3K9Ywf%`Bg}d$}sl5&nlgB(C z5;7OP%X!mi5QyRlalOBY_YjGt!DdDPr2&c^=z~oX_2$gzu)FG?1q-hjk_Xm9lPFtG zuvXq*HbjqI#N4j$C5p!X1)&k1cULR3rlg`ga4#p2HAggYa`xAsv0Z6Y`z}MC%c7wD z3W$jslIEnT_HLh*Zha>#wpdWhYQ+yO@wElMtO(SrrtT02$c> zk}yach!j8^KCsN0gN)J7vSM4_@sSwrmX}x*_RZX;(!TuNZGZh23ugEkSx5vPh3DKY zd@+|KGu|WGBfqBDHbW0O{?I|>!zAm0No2%`D-0F8^e_EJl_}eXXI1Ry*-4V@ zVY(E~rGH(MjQ<3q$_vwWHCP5jHa0zoM2z%z0Lb%X2)Ea8)T!~+ht$ep*E6CF$;s=ZS1=0hWiFar2juKA+;AK&` zM5YndA%Gb=>Wkswt;NDvtUJMDQyTi?zCsm#*D(;=yimbmc#`!Q-RTRjJa{01L*&_0 zY&Jf2}N;F{j?am3B+Etp9?iMeVgiUYeNitjP6CCr1yxW zSS2p*BgVD@0TNUU#0A(A0Uy(!5}#1nzrx@!*Od0Hrz{2{Fu`>LAdgPaXQGm?`2YtfgF9Mg{wbi$8-T$C{Gr9BTP7$WYoI+;k7>}eR)-*BfvuiHs< z=;ZrUSbSFOOV~mHcPf*BpbuWh63q@5_g7(5i*blQ_#I>%B9-?%K3}A8_$BzYAYuxl zU;i}&n0oF$3-)u`KU8R)eV7Eb52pSA3z=K4jPA7HpN*FpK~q(~ZfL;&{_0F`Dip5{ z{QVA`^FqvCfcYdiwY?tMfm&@cetRAo1YiiJh!^sF&xfPWVEPXE6YM$zS^^AVdx{5* z2~Z^9{d*gkF+nC9rXnC{2mE+LGivnLCq%(a-}jTDvW&IJU|c$_&<-(zVe1R2un?k= zZlo?U&?M}K@3mpW8S=XzR6SlEXGH7erf3~`ikJrey9|cDy#>T>=o!?f2 zzVTKTnDtf)Wy4S;mmX|R;1h~&jQIzQL?RRn%!-ZcC`1fsE&fzpnHpmJF}Bl966#FE zgcgAXlF+JvzoLCH*5t&Hi|z?K zZb+ZY?xP*Vqk~zFhW;HcUbB}GEi6e4-UnLOF8Lj6SVGb`VlM1w0aU94kqG(*53lD4 zL$xs@wnT{VfQgsPO*3kgyVXGip|Z!XJ~S#+ucFphhfko^&OTOj2H+p|UjAtyrnXDp zFb~{b5EmV4Bf<|m664$LI!|aQlPDlf{F}^2C%Q5++?uO`JKecqXt3GSJG=2b5rOxB z%J3U7y#+5RmKGNC&IY}ps7FOu6NI5BZJ2MMWYoQ6mM!;$bJ;uUlcC%N1DipULtHL- zwI1ZqkpYLA1ol0`ZHR`6yy*@GdroTjJQT0JqOMFbAg0nDnE4E&$%G_|d~B11MgQHo z4bfYm;g@_QOg$`U#@-SgYDiU6C?O+L&BpSO6YaMPk3Pr{D-h}Y7%1D=L1!Yb!NX<# z@Up=(h+i--irN>3#{!#A(!eJK?NRwaW+<&0;(CwViR-ch@+F=E4ww@Z2XBiT21OYEknsM&oxw05aVG5bjF#Fe}!(6}dI}9+h@g zC$)F$Vg-zCh>iwTMocW@O)pn_P!RRx_$u_ucQ;?;cI(z>myjL=BO|QP)>n6(I%`YR z6LAcp55OBi{tew{xf}_huHZ|sv3d%oD_Ycm@P0^8?(0L~L=>fgpcera@q*PP#%!U; z-LMKS>rIh|N)?X`W`T*yUm6f(W*qWCBHm*4{wD~zx&cHa6C|M00*nhHdtRItfUn?R z?(#@xLC)02f~1PjM>tnqE@Xmb0ieNPXt$?OLz!Fv!;>$CJowqZ6a(%4!2@Z!IBd&= zufrfIx$hc=rv|xP03)(qU{08Def)>>A60kh%X=Z1){`;SLwf-UNHr$o7`2-06TC8&T!VxR`3C`yi!$-4)P7kE+@LOjT^6;ex7Z`y65e_7VlxIK$(AEdw{1c9Z^zTGwYJ@CY zgMRu0gl(Dkkb?iQGmkms*zCmE{Xz&*gEvK#Sw>Lq7Fl;2Bzr{8TNp` z-i7U;U^y&$a%oW>DHtJLR)5(ogj;)-<$!;_vmO!1y9@XBl!UFGhr970^X~Kskm-LC zKFU$^Q#g{1Ia4PJgbC#Fw)rU{GrZC}V-}_?kF2 z*b<3K1z(lgWmP24NP!M3L~06>2!7KhBi;jML*NA$ zYb(T1w;Ex#O9w`>Z$(0p!P^Ur2GT@EI`OYVH{SW9Sr|eZkXj^{=Q~JSD*@&M5Udw+ z{+i&CgE?8pGuJu*5li!$qwVof^0vbO7Ne@*Y*a3e=+VlrGoKBugPM^f%fMR^1SiTf z^b!V$r~2#2=TArALp3o;^pZqHr zf;*CZ<5xj{FOG%eF?`LY!iT^N4N-nC6WS9tNoW}&$>KjM<%!qB@L=L(7wlPD4rG$X~3YVAmQML1d-Q7 zghBtmUcEa4Z%s^<2U0``Bc|c-%59=XU%G3OA?_H?Rh1&}=Z+y#2_q+9`72=TMq;fL zP|Z<`4ZUnahT?7~>eLi6Aag^WbFsvH`d^*y1(pX`*+VG8!1)e{zhE+3@larQ#rc0{ z6X|rqIB$PwKM7NKg()I~b#W8iR7C@EsqovK39f6mWbc!g{ZX zNlX;^pzrATFe_zp~<9r zm=XCv+kxR6GpAqy_-rKjKk@~i@KGmn{4|ud!5?HHtErgdoZZ(Z3B&z- z$rqEPObprg>A;TFL)7x!xk<#F6eeL#ub|xf*bvl5+E@bwmHjUl5Q#O+fuBTI0`$?1 z=mW{X(=E>++UiJnOix`1@8e-nNDBL&to8AGDw6k0J}9kU^n<;~D#3B>wUjn831L*B zQ0W1(dwi;&pTfKkF%__A(lI_pgCl_QdzB76;4@ zrC=T-bdzOLUi{Rs_|1p*sUM6X79QN@#Hr&G8G#6(&K>ju;7C=5NwPZEDG& zrSwcA$P*K2!?`j)U1rQx?ZYIgWKQQf3AjPL7zz~g9zcfdw@BV52kLTwgzTUYd@a(T z%!Ywf0?J%_G@{N2EFRRk6#{AX`QyYm!R?()p@~7v?>7{j-%|m*+RjiI%GiyhnIP@w?i}WZctO18{yC?IE`pa!9^gKk8d4t>Wy_6R0 zskN>!BZ9IJX9dCizm>LTYT_R=7eMw9p7SU|e zAEjY;hXUSZckAqs`;v`@$sr`C7_;UfSzLu(Ls7r4Y0`6B@TRO!uioRANh2mK523C% zT5=*$fJ)>F56QhBL?T+e#X3C~j_3zpLal~9#TaCF{1HTyzz&!GF&PRBDj)!imR>0Z z6Ys*r>IV~QriF*9Q;4N@=<&l1&x$QtY=YYpP$BBwHB&Noq%`{ZS|^f37%*oW;XCu* zPy)Q=L8lHs)~u<2&|S3hwY5eQGt=f{S08G)wBFH12_I9MnT|b7ED0D*=TljatF{*) zPoj-#oVe7j&%<8wz-J(o;8P7$JGQfSLlSCQ!8RfX>fvYm^YjZ}k$Z=I7?rOZFC*iz z&%TFj^iZ}~I;jZ zT}Iem2Fi8Zes6{L%wHq2U1rVFl7yNvZiWbAV^d zQF$&%uVeKCf(_Uq-}{}3w4>RNX42yU*kA}tRQg@VW`T~<*}E5&HsksCwGkbKjjb>! z3tC(FHS26ndl#~OFh)dx_ge?%={AW=r(%Y)J-<0Tb-DxyQfdaHwXIlU$t!8g5z0S zRTb`51Io0EJH4f`qi>T1Q7@WKt)vBdx7BA?T_aB>o5EkW_EJ8{;C!N(^gH?x>h6%& zorVt&d6}v5G7~*6O6um9MQC$B7;wHhn?;jjMM?!WyHY7XWi%`L^|CD!N|pY!(A^~V z5#NN@;>we@Il8aj>kw)J8S$S<*A#x~M&u|v+PR6Wpn|wQi{)W!wZ~K@>zI9ouaZzH zBptrbe&*K+O?3a0fKNhoT=>s{d>g;6%l&eBfwGOd$8q#VQ5v-0>S;%Yp=a=Yd;2qK zGG(Z|@+_K%fVxi^ssN47ov@gtNc+rrRl@7=FFbRe;|w_}S*r~TY0`a1PyHeL*gr_e2{e1m1^IA-7TCl%Q4Ecl=qaPZnU{cm<9 zmt#4}-EC&G!fTy3*e=8O*Ppo&zD-IUELy)vH)Lh&cJ4sM^6$(0vOJv0KD$`m#fRih zUo4e>wg2s5JJhz?Y@Jv6*J0Zt-N_>drPDA3{ESZur3J<^uY}#Fijr*bctdT(Wt2r5 zv%-)?!U&rCpzqJ?Zju(Wn!&GpQ6@jtqtX_qFM8A!dBcyM6oWs1z-cTPuBfBWWdFkDlkj)(-j%M>NlQLGjFSwG@KCVjE<19t z`TErX4$99rWF_l`)+l6GM%J#tpNp|QL8UOnT<7Geca`$G{MtxM^c$J7u64!VO*K(r z02Q;NY(`;LMJ882IC*N45M+OkEUYiL9_Rk)Nb?>b;A9*1vRUJEUCSo{Zs&;Cs5sh6 z&ut}-8=FNe0gN{TN4gcWQ*|zUlA_!up From e19f1d8db8d6007318884272b4b04fcc97bab76f Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Tue, 13 Aug 2013 14:51:31 -0700 Subject: [PATCH 040/104] v6036 Change-Id: I7e70d0e2e367f703fe420d4300eb5fecdc247099 --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 6dbfb9f93..eb6f0fab8 100644 --- a/Android.mk +++ b/Android.mk @@ -40,7 +40,7 @@ RECOVERY_NAME := CWM-based Recovery endif endif -RECOVERY_VERSION := $(RECOVERY_NAME) v6.0.3.5 +RECOVERY_VERSION := $(RECOVERY_NAME) v6.0.3.6 LOCAL_CFLAGS += -DRECOVERY_VERSION="$(RECOVERY_VERSION)" RECOVERY_API_VERSION := 2 From 6bdc5c60bd9069367de4fea187c4e54fd5ce1ca4 Mon Sep 17 00:00:00 2001 From: philz-cwm6 Date: Wed, 14 Aug 2013 18:25:12 +0200 Subject: [PATCH 041/104] Enable mount usb for external storage on datamedia devices Change-Id: I73b4112983b7b118d738eb224b32f4f0beb5d147 --- extendedcommands.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/extendedcommands.c b/extendedcommands.c index 02abf0ab4..286be6c8e 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -921,12 +921,12 @@ void show_partition_menu() } if (!is_data_media()) { - options[mountable_volumes + formatable_volumes] = "mount USB storage"; - options[mountable_volumes + formatable_volumes + 1] = NULL; - } - else { - options[mountable_volumes + formatable_volumes] = "format /data and /data/media (/sdcard)"; - options[mountable_volumes + formatable_volumes + 1] = NULL; + options[mountable_volumes + formatable_volumes] = "mount USB storage"; + options[mountable_volumes + formatable_volumes + 1] = NULL; + } else { + options[mountable_volumes + formatable_volumes] = "format /data and /data/media (/sdcard)"; + options[mountable_volumes + formatable_volumes + 1] = "mount USB storage"; + options[mountable_volumes + formatable_volumes + 2] = NULL; } int chosen_item = get_menu_selection(headers, &options, 0, 0); @@ -948,6 +948,9 @@ void show_partition_menu() handle_data_media_format(0); } } + else if (is_data_media() && chosen_item == (mountable_volumes+formatable_volumes+1)) { + show_mount_usb_storage_menu(); + } else if (chosen_item < mountable_volumes) { MountMenuEntry* e = &mount_menu[chosen_item]; Volume* v = e->v; From 5a650a98077abf5073116a83a6f5d473dca2e7bb Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Thu, 15 Aug 2013 17:55:04 -0700 Subject: [PATCH 042/104] Fix issue where android device manager, etc, dont actually wipe data. --wipe_data issues via /cache/recovery/command will now completely format data. All other manual wipe commands will preserve data on /data/media. There is another mounts/storage option that will do a true data format. Change-Id: Ie8ecd2b0e14c3bb1d8a404ea868cdf703455d2ab --- Android.mk | 2 +- extendedcommands.c | 4 ++-- recovery.c | 4 ++++ roots.c | 11 ++++++----- roots.h | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Android.mk b/Android.mk index eb6f0fab8..5b78cc743 100644 --- a/Android.mk +++ b/Android.mk @@ -40,7 +40,7 @@ RECOVERY_NAME := CWM-based Recovery endif endif -RECOVERY_VERSION := $(RECOVERY_NAME) v6.0.3.6 +RECOVERY_VERSION := $(RECOVERY_NAME) v6.0.3.7 LOCAL_CFLAGS += -DRECOVERY_VERSION="$(RECOVERY_VERSION)" RECOVERY_API_VERSION := 2 diff --git a/extendedcommands.c b/extendedcommands.c index 02abf0ab4..a78e6d67d 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -939,13 +939,13 @@ void show_partition_menu() else { if (!confirm_selection("format /data and /data/media (/sdcard)", confirm)) continue; - handle_data_media_format(1); + ignore_data_media_workaround(1); ui_print("Formatting /data...\n"); if (0 != format_volume("/data")) ui_print("Error formatting /data!\n"); else ui_print("Done.\n"); - handle_data_media_format(0); + ignore_data_media_workaround(0); } } else if (chosen_item < mountable_volumes) { diff --git a/recovery.c b/recovery.c index 560b1f950..bd6fadac9 100644 --- a/recovery.c +++ b/recovery.c @@ -774,7 +774,9 @@ setup_adbd() { check_and_fclose(file_src, key_src); } } + ignore_data_media_workaround(1); ensure_path_unmounted("/data"); + ignore_data_media_workaround(0); // Trigger (re)start of adb daemon property_set("service.adb.root", "1"); @@ -942,7 +944,9 @@ main(int argc, char **argv) { if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n"); } else if (wipe_data) { if (device_wipe_data()) status = INSTALL_ERROR; + ignore_data_media_workaround(1); if (erase_volume("/data")) status = INSTALL_ERROR; + ignore_data_media_workaround(0); if (has_datadata() && erase_volume("/datadata")) status = INSTALL_ERROR; if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR; if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n"); diff --git a/roots.c b/roots.c index fc8dc0f94..8fe9632ec 100644 --- a/roots.c +++ b/roots.c @@ -211,9 +211,11 @@ int ensure_path_mounted_at_mount_point(const char* path, const char* mount_point return -1; } +static int ignore_data_media = 0; + int ensure_path_unmounted(const char* path) { // if we are using /data/media, do not ever unmount volumes /data or /sdcard - if (strstr(path, "/data") == path && is_data_media()) { + if (strstr(path, "/data") == path && is_data_media() && !ignore_data_media) { return 0; } @@ -248,7 +250,6 @@ int ensure_path_unmounted(const char* path) { } extern struct selabel_handle *sehandle; -static int handle_data_media = 0; int format_volume(const char* volume) { Volume* v = volume_for_path(volume); @@ -264,7 +265,7 @@ int format_volume(const char* volume) { } // check to see if /data is being formatted, and if it is /data/media // Note: the /sdcard check is redundant probably, just being safe. - if (strstr(volume, "/data") == volume && is_data_media() && !handle_data_media) { + if (strstr(volume, "/data") == volume && is_data_media() && !ignore_data_media) { return format_unknown_device(NULL, volume, NULL); } if (strcmp(v->fs_type, "ramdisk") == 0) { @@ -324,6 +325,6 @@ int format_volume(const char* volume) { return format_unknown_device(v->blk_device, volume, v->fs_type); } -void handle_data_media_format(int handle) { - handle_data_media = handle; +void ignore_data_media_workaround(int ignore) { + ignore_data_media = ignore; } diff --git a/roots.h b/roots.h index e9af4b620..4b139bf8f 100644 --- a/roots.h +++ b/roots.h @@ -46,6 +46,6 @@ Volume* get_device_volumes(); int is_data_media(); void setup_data_media(); int is_data_media_volume_path(const char* path); -void handle_data_media_format(int handle); +void ignore_data_media_workaround(int ignore); #endif // RECOVERY_ROOTS_H_ From aee5f78ddec238cec016849acaf1d3007b8b1507 Mon Sep 17 00:00:00 2001 From: philz-cwm6 Date: Fri, 16 Aug 2013 23:09:00 +0200 Subject: [PATCH 043/104] If sd-ext is defined in recovery.fstab, but user did not partition sdcard, silent failure while formatting device (every nandroid restore or wipe data) Change-Id: I3ee2f891899be8e4fe0d722e64357dc5e7bc7428 --- roots.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/roots.c b/roots.c index 8fe9632ec..45b1c8a7d 100644 --- a/roots.c +++ b/roots.c @@ -255,11 +255,19 @@ int format_volume(const char* volume) { Volume* v = volume_for_path(volume); if (v == NULL) { // silent failure for sd-ext - if (strcmp(volume, "/sd-ext") == 0) - return -1; - LOGE("unknown volume \"%s\"\n", volume); + if (strcmp(volume, "/sd-ext") != 0) + LOGE("unknown volume \"%s\"\n", volume); return -1; } + // silent failure to format non existing sd-ext when defined in recovery.fstab + if (strcmp(volume, "/sd-ext") == 0) { + struct stat s; + if (0 != stat(v->blk_device, &s)) { + LOGI("Skipping format of sd-ext\n"); + return -1; + } + } + if (is_data_media_volume_path(volume)) { return format_unknown_device(NULL, volume, NULL); } From 03667f68f8b32c3abf5ac006c2b4776159b71521 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sat, 17 Aug 2013 23:42:57 -0700 Subject: [PATCH 044/104] recovery: Only install adb keys once * Recovery is currently calling setup_adbd when the process starts to fetch the keys from the data partition and (re)start adbd. * This of course has the heinous side effect of making it impossible to use gdbserver to debug recovery, making me a sad panda. * Make setup_adbd a pseudo-binary, and run it as a oneshot service instead. Change-Id: Ib4b9e342564bb60bdc2df4cee91566f31c7db8f1 --- Android.mk | 2 +- etc/init.rc | 7 ++++--- recovery.c | 7 +++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Android.mk b/Android.mk index 5b78cc743..dc30e2323 100644 --- a/Android.mk +++ b/Android.mk @@ -107,7 +107,7 @@ LOCAL_STATIC_LIBRARIES += libselinux include $(BUILD_EXECUTABLE) -RECOVERY_LINKS := bu make_ext4fs edify busybox flash_image dump_image mkyaffs2image unyaffs erase_image nandroid reboot volume setprop getprop dedupe minizip +RECOVERY_LINKS := bu make_ext4fs edify busybox flash_image dump_image mkyaffs2image unyaffs erase_image nandroid reboot volume setprop getprop dedupe minizip setup_adbd # nc is provided by external/netcat RECOVERY_SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(RECOVERY_LINKS)) diff --git a/etc/init.rc b/etc/init.rc index 241c29219..ef240aad1 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -49,12 +49,13 @@ service ueventd /sbin/ueventd service recovery /sbin/recovery +service setup_adbd /sbin/setup_adbd + oneshot + service adbd /sbin/adbd recovery disabled -# Recovery will start adb once it has checked the keys - -# Restart adbd so it can run as root +# setup_adbd will start adb once it has checked the keys on property:service.adb.root=1 write /sys/class/android_usb/android0/enable 0 restart adbd diff --git a/recovery.c b/recovery.c index bd6fadac9..094b90891 100644 --- a/recovery.c +++ b/recovery.c @@ -843,6 +843,11 @@ main(int argc, char **argv) { return setprop_main(argc, argv); if (strstr(argv[0], "getprop")) return getprop_main(argc, argv); + if (strstr(argv[0], "setup_adbd")) { + load_volume_table(); + setup_adbd(); + return 0; + } return busybox_driver(argc, argv); } __system("/sbin/postrecoveryboot.sh"); @@ -989,8 +994,6 @@ main(int argc, char **argv) { } } - setup_adbd(); - if (headless) { headless_wait(); } From efa3b635d821c4e099a36216fd6928b3bdea94fe Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Fri, 16 Aug 2013 18:00:21 -0700 Subject: [PATCH 045/104] recovery: Revenge of MiniVold * Vold has been refactored to be friendly to an environment such as recovery. Using Vold will eliminate many hacks and add previously unavailable features. * Link with libminivold and create a pseudo-binary to launch as a service in recovery. * Libvoldclient is an event-driven client for controlling the daemon. * Handles device hotplug and automatic mounting, if desired. * Manages USB storage * Can check and format volumes using any supported filesystem * Dynamically generate all storage paths * Dynamically refresh menus affected by storage changes * Fixed wall-of-compile-warnings and const-correctness in extendedcommands.c * Misc cleanups * Say goodbye to "apply /sdcard/update.zip". You have served us well. Change-Id: I13a3d72493f8fce7c19f192f9e94ad22cb60a2d2 --- Android.mk | 21 +- edifyscripting.c | 13 +- etc/init.rc | 26 +- extendedcommands.c | 691 +++++++++++++++++----------------------- extendedcommands.h | 19 +- minui/Android.mk | 6 + mounts.c | 6 +- nandroid.c | 12 +- recovery.c | 133 ++++++-- recovery_ui.h | 3 +- roots.c | 90 +++++- roots.h | 7 + ui.c | 20 +- voldclient/Android.mk | 14 + voldclient/commands.c | 108 +++++++ voldclient/dispatcher.c | 231 ++++++++++++++ voldclient/event_loop.c | 275 ++++++++++++++++ voldclient/voldclient.h | 50 +++ 18 files changed, 1266 insertions(+), 459 deletions(-) create mode 100644 voldclient/Android.mk create mode 100644 voldclient/commands.c create mode 100644 voldclient/dispatcher.c create mode 100644 voldclient/event_loop.c create mode 100644 voldclient/voldclient.h diff --git a/Android.mk b/Android.mk index dc30e2323..52258cea8 100644 --- a/Android.mk +++ b/Android.mk @@ -15,12 +15,14 @@ LOCAL_SRC_FILES := \ nandroid.c \ ../../system/core/toolbox/reboot.c \ ../../system/core/toolbox/dynarray.c \ + ../../system/core/toolbox/newfs_msdos.c \ firmware.c \ edifyscripting.c \ prop.c \ default_recovery_ui.c \ adb_install.c \ - verifier.c + verifier.c \ + ../../system/vold/vdc.c ADDITIONAL_RECOVERY_FILES := $(shell echo $$ADDITIONAL_RECOVERY_FILES) LOCAL_SRC_FILES += $(ADDITIONAL_RECOVERY_FILES) @@ -71,8 +73,10 @@ $(foreach board_define,$(BOARD_RECOVERY_DEFINES), \ LOCAL_STATIC_LIBRARIES := -LOCAL_CFLAGS += -DUSE_EXT4 -LOCAL_C_INCLUDES += system/extras/ext4_utils system/core/fs_mgr/include +LOCAL_CFLAGS += -DUSE_EXT4 -DMINIVOLD +LOCAL_C_INCLUDES += system/extras/ext4_utils system/core/fs_mgr/include external/fsck_msdos +LOCAL_C_INCLUDES += system/vold system/core/include system/core/libcutils + LOCAL_STATIC_LIBRARIES += libext4_utils_static libz libsparse_static # This binary is in the recovery ramdisk, which is otherwise a copy of root. @@ -89,17 +93,23 @@ else endif LOCAL_STATIC_LIBRARIES += libmake_ext4fs libext4_utils_static libz libsparse_static +LOCAL_STATIC_LIBRARIES += libminivold libvoldclient +LOCAL_STATIC_LIBRARIES += libsysutils libdiskconfig libext2_blkid libext2_uuid liblogwrap libpower + +LOCAL_STATIC_LIBRARIES += libcrypto_static LOCAL_STATIC_LIBRARIES += libminzip libunz libmincrypt LOCAL_STATIC_LIBRARIES += libminizip libminadbd libedify libbusybox libmkyaffs2image libunyaffs liberase_image libdump_image libflash_image LOCAL_LDFLAGS += -Wl,--no-fatal-warnings -LOCAL_STATIC_LIBRARIES += libfs_mgr libdedupe libcrypto_static libcrecovery libflashutils libmtdutils libmmcutils libbmlutils +LOCAL_STATIC_LIBRARIES += libfs_mgr libdedupe libcrecovery libflashutils libmtdutils libmmcutils libbmlutils ifeq ($(BOARD_USES_BML_OVER_MTD),true) LOCAL_STATIC_LIBRARIES += libbml_over_mtd endif +LOCAL_STATIC_LIBRARIES += libfsck_msdos + LOCAL_STATIC_LIBRARIES += libminui libpixelflinger_static libpng libcutils liblog LOCAL_STATIC_LIBRARIES += libstdc++ libc @@ -107,7 +117,7 @@ LOCAL_STATIC_LIBRARIES += libselinux include $(BUILD_EXECUTABLE) -RECOVERY_LINKS := bu make_ext4fs edify busybox flash_image dump_image mkyaffs2image unyaffs erase_image nandroid reboot volume setprop getprop dedupe minizip setup_adbd +RECOVERY_LINKS := bu make_ext4fs edify busybox flash_image dump_image mkyaffs2image unyaffs erase_image nandroid reboot volume setprop getprop start stop dedupe minizip setup_adbd fsck_msdos newfs_msdos minivold vdc # nc is provided by external/netcat RECOVERY_SYMLINKS := $(addprefix $(TARGET_RECOVERY_ROOT_OUT)/sbin/,$(RECOVERY_LINKS)) @@ -181,4 +191,5 @@ include $(commands_recovery_local_path)/updater/Android.mk include $(commands_recovery_local_path)/applypatch/Android.mk include $(commands_recovery_local_path)/utilities/Android.mk include $(commands_recovery_local_path)/su/Android.mk +include $(commands_recovery_local_path)/voldclient/Android.mk commands_recovery_local_path := diff --git a/edifyscripting.c b/edifyscripting.c index 68ac4f047..440d5f2f2 100644 --- a/edifyscripting.c +++ b/edifyscripting.c @@ -153,7 +153,7 @@ Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { free(path); return StringValue(strdup("")); } - if (0 != format_volume("/sdcard/.android_secure")) { + if (0 != format_volume(get_android_secure_path())) { free(path); return StringValue(strdup("")); } @@ -313,6 +313,7 @@ int run_script_from_buffer(char* script_data, int script_len, char* filename) int run_and_remove_extendedcommand() { + char* primary_path = get_primary_storage_path(); char tmp[PATH_MAX]; sprintf(tmp, "cp %s /tmp/%s", EXTENDEDCOMMAND_SCRIPT, basename(EXTENDEDCOMMAND_SCRIPT)); __system(tmp); @@ -320,7 +321,7 @@ int run_and_remove_extendedcommand() int i = 0; for (i = 20; i > 0; i--) { ui_print("Waiting for SD Card to mount (%ds)\n", i); - if (ensure_path_mounted("/sdcard") == 0) { + if (ensure_path_mounted(primary_path) == 0) { ui_print("SD Card mounted...\n"); break; } @@ -337,14 +338,14 @@ int run_and_remove_extendedcommand() ui_print("SD Card marker not found...\n"); if (volume_for_path("/emmc") != NULL) { ui_print("Checking Internal SD Card marker...\n"); - ensure_path_unmounted("/sdcard"); - if (ensure_path_mounted_at_mount_point("/emmc", "/sdcard") != 0) { + ensure_path_unmounted(primary_path); + if (ensure_path_mounted_at_mount_point("/emmc", primary_path) != 0) { ui_print("Internal SD Card marker not found... continuing anyways.\n"); // unmount everything, and remount as normal ensure_path_unmounted("/emmc"); - ensure_path_unmounted("/sdcard"); + ensure_path_unmounted(primary_path); - ensure_path_mounted("/sdcard"); + ensure_path_mounted(primary_path); } } } diff --git a/etc/init.rc b/etc/init.rc index ef240aad1..ea0faac4f 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -27,10 +27,30 @@ on init chown root shell /tmp chmod 0775 /tmp + mkdir /mnt 0775 root system + mkdir /storage 0050 root sdcard_r + mount tmpfs tmpfs /storage mode=0050,uid=0,gid=1028 + + # See storage config details at http://source.android.com/tech/storage/ + mkdir /mnt/shell 0700 shell shell + + # Directory for putting things only root should see. + mkdir /mnt/secure 0700 root root + + # Create private mountpoint so we can MS_MOVE from staging + mount tmpfs tmpfs /mnt/secure mode=0700,uid=0,gid=0 + + # Directory for staging bindmounts + mkdir /mnt/secure/staging 0700 root root + + # Fuse public mount points. + mkdir /mnt/fuse 0700 root system + mount tmpfs tmpfs /mnt/fuse mode=0775,gid=1000 + write /sys/class/android_usb/android0/enable 0 write /sys/class/android_usb/android0/idVendor 18D1 write /sys/class/android_usb/android0/idProduct D001 - write /sys/class/android_usb/android0/functions adb + write /sys/class/android_usb/android0/functions adb,mass_storage write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer} write /sys/class/android_usb/android0/iProduct ${ro.product.model} write /sys/class/android_usb/android0/iSerial ${ro.serialno} @@ -55,6 +75,10 @@ service setup_adbd /sbin/setup_adbd service adbd /sbin/adbd recovery disabled +service vold /sbin/minivold + socket vold stream 0660 root mount + ioprio be 2 + # setup_adbd will start adb once it has checked the keys on property:service.adb.root=1 write /sys/class/android_usb/android0/enable 0 diff --git a/extendedcommands.c b/extendedcommands.c index 31a971c07..77561987d 100644 --- a/extendedcommands.c +++ b/extendedcommands.c @@ -40,15 +40,17 @@ #include "mtdutils/mtdutils.h" #include "bmlutils/bmlutils.h" #include "cutils/android_reboot.h" +#include "mmcutils/mmcutils.h" +#include "voldclient/voldclient.h" #include "adb_install.h" int signature_check_enabled = 1; int script_assert_enabled = 1; -static const char *SDCARD_UPDATE_FILE = "/sdcard/update.zip"; +static const char *SDCARD_UPDATE_FILE = "update.zip"; int -get_filtered_menu_selection(char** headers, char** items, int menu_only, int initial_selection, int items_count) { +get_filtered_menu_selection(const char** headers, char** items, int menu_only, int initial_selection, int items_count) { int index; int offset = 0; int* translate_table = (int*)malloc(sizeof(int) * items_count); @@ -88,10 +90,9 @@ void write_string_to_file(const char* filename, const char* string) { } void write_recovery_version() { - if ( is_data_media() ) { - write_string_to_file("/sdcard/0/clockworkmod/.recovery_version",EXPAND(RECOVERY_VERSION) "\n" EXPAND(TARGET_DEVICE)); - } - write_string_to_file("/sdcard/clockworkmod/.recovery_version",EXPAND(RECOVERY_VERSION) "\n" EXPAND(TARGET_DEVICE)); + char path[PATH_MAX]; + sprintf(path, "%s%sclockworkmod/.recovery_version", get_primary_storage_path(), (is_data_media() ? "/0/" : "/")); + write_string_to_file(path,EXPAND(RECOVERY_VERSION) "\n" EXPAND(TARGET_DEVICE)); } void @@ -121,64 +122,71 @@ int install_zip(const char* packagefilepath) #define ITEM_CHOOSE_ZIP 0 #define ITEM_APPLY_SIDELOAD 1 -#define ITEM_APPLY_UPDATE 2 // /sdcard/update.zip -#define ITEM_SIG_CHECK 3 -#define ITEM_CHOOSE_ZIP_INT 4 +#define ITEM_SIG_CHECK 2 +#define ITEM_CHOOSE_ZIP_INT 3 -void show_install_update_menu() +int show_install_update_menu() { - static char* headers[] = { "Install update from zip file", + char buf[100]; + int i = 0, chosen_item = 0; + static char* install_menu_items[MAX_NUM_MANAGED_VOLUMES + 3]; + + char* primary_path = get_primary_storage_path(); + char** extra_paths = get_extra_storage_paths(); + int num_extra_volumes = get_num_extra_volumes(); + + memset(install_menu_items, 0, MAX_NUM_MANAGED_VOLUMES + 3); + + static const char* headers[] = { "Install update from zip file", "", NULL }; - - char* install_menu_items[] = { "choose zip from sdcard", - "install zip from sideload", - "apply /sdcard/update.zip", - "toggle signature verification", - NULL, - NULL }; - - char *other_sd = NULL; - if (volume_for_path("/emmc") != NULL) { - other_sd = "/emmc/"; - install_menu_items[4] = "choose zip from internal sdcard"; - } - else if (volume_for_path("/external_sd") != NULL) { - other_sd = "/external_sd/"; - install_menu_items[4] = "choose zip from external sdcard"; + + sprintf(buf, "choose zip from %s", primary_path); + install_menu_items[0] = strdup(buf); + + install_menu_items[1] = "install zip from sideload"; + + install_menu_items[2] = "toggle signature verification"; + + install_menu_items[3 + num_extra_volumes] = NULL; + + for (i = 0; i < num_extra_volumes; i++) { + sprintf(buf, "choose zip from %s", extra_paths[i]); + install_menu_items[3 + i] = strdup(buf); } - + for (;;) { - int chosen_item = get_menu_selection(headers, install_menu_items, 0, 0); + chosen_item = get_menu_selection(headers, install_menu_items, 0, 0); switch (chosen_item) { case ITEM_SIG_CHECK: toggle_signature_check(); break; - case ITEM_APPLY_UPDATE: - { - if (confirm_selection("Confirm install?", "Yes - Install /sdcard/update.zip")) - install_zip(SDCARD_UPDATE_FILE); - break; - } case ITEM_CHOOSE_ZIP: - show_choose_zip_menu("/sdcard/"); + show_choose_zip_menu(primary_path); write_recovery_version(); break; case ITEM_APPLY_SIDELOAD: apply_from_adb(); break; - case ITEM_CHOOSE_ZIP_INT: - if (other_sd != NULL) - show_choose_zip_menu(other_sd); - break; default: - return; + if (chosen_item >= ITEM_CHOOSE_ZIP_INT) { + show_choose_zip_menu(extra_paths[chosen_item - 3]); + } else { + goto out; + } } - } +out: + // free all the dynamic items + free(install_menu_items[0]); + if (extra_paths != NULL) { + for (i = 0; i < num_extra_volumes; i++) + free(install_menu_items[3 + i]); + } + return chosen_item; } void free_string_array(char** array) @@ -213,7 +221,7 @@ char** gather_files(const char* directory, const char* fileExtensionOrDirectory, return NULL; } - int extension_length = 0; + unsigned int extension_length = 0; if (fileExtensionOrDirectory != NULL) extension_length = strlen(fileExtensionOrDirectory); @@ -297,8 +305,9 @@ char** gather_files(const char* directory, const char* fileExtensionOrDirectory, } // pass in NULL for fileExtensionOrDirectory and you will get a directory chooser -char* choose_file_menu(const char* directory, const char* fileExtensionOrDirectory, const char* headers[]) +char* choose_file_menu(const char* basedir, const char* fileExtensionOrDirectory, const char* headers[]) { + static const char* fixed_headers[20]; char path[PATH_MAX] = ""; DIR *dir; struct dirent *de; @@ -306,13 +315,21 @@ char* choose_file_menu(const char* directory, const char* fileExtensionOrDirecto int numDirs = 0; int i; char* return_value = NULL; - int dir_len = strlen(directory); + char directory[PATH_MAX]; + int dir_len = strlen(basedir); + + strcpy(directory, basedir); + + // Append a traiing slash if necessary + if (directory[dir_len - 1] != '/') { + strcat(directory, "/"); + dir_len++; + } i = 0; while (headers[i]) { i++; } - const char** fixed_headers = (const char*)malloc((i + 3) * sizeof(char*)); i = 0; while (headers[i]) { fixed_headers[i] = headers[i]; @@ -350,7 +367,7 @@ char* choose_file_menu(const char* directory, const char* fileExtensionOrDirecto for (;;) { int chosen_item = get_menu_selection(fixed_headers, list, 0, 0); - if (chosen_item == GO_BACK) + if (chosen_item == GO_BACK || chosen_item == REFRESH) break; static char ret[PATH_MAX]; if (chosen_item < numDirs) @@ -373,7 +390,6 @@ char* choose_file_menu(const char* directory, const char* fileExtensionOrDirecto free_string_array(files); free_string_array(dirs); - free(fixed_headers); return return_value; } @@ -384,7 +400,7 @@ void show_choose_zip_menu(const char *mount_point) return; } - static char* headers[] = { "Choose a zip to apply", + static const char* headers[] = { "Choose a zip to apply", "", NULL }; @@ -406,7 +422,7 @@ void show_nandroid_restore_menu(const char* path) return; } - static char* headers[] = { "Choose an image to restore", + static const char* headers[] = { "Choose an image to restore", "", NULL }; @@ -428,7 +444,7 @@ void show_nandroid_delete_menu(const char* path) return; } - static char* headers[] = { "Choose an image to delete", + static const char* headers[] = { "Choose an image to delete", "", NULL }; @@ -446,147 +462,32 @@ void show_nandroid_delete_menu(const char* path) } } -#define MAX_NUM_USB_VOLUMES 3 -#define LUN_FILE_EXPANDS 2 - -struct lun_node { - const char *lun_file; - struct lun_node *next; -}; - -static struct lun_node *lun_head = NULL; -static struct lun_node *lun_tail = NULL; - -int control_usb_storage_set_lun(Volume* vol, bool enable, const char *lun_file) { - const char *vol_device = enable ? vol->blk_device : ""; - int fd; - struct lun_node *node; - - // Verify that we have not already used this LUN file - for(node = lun_head; node; node = node->next) { - if (!strcmp(node->lun_file, lun_file)) { - // Skip any LUN files that are already in use - return -1; - } - } - - // Open a handle to the LUN file - LOGI("Trying %s on LUN file %s\n", vol->blk_device, lun_file); - if ((fd = open(lun_file, O_WRONLY)) < 0) { - LOGW("Unable to open ums lunfile %s (%s)\n", lun_file, strerror(errno)); - return -1; - } - - // Write the volume path to the LUN file - if ((write(fd, vol_device, strlen(vol_device) + 1) < 0) && - (!enable || !vol->blk_device2 || (write(fd, vol->blk_device2, strlen(vol->blk_device2)) < 0))) { - LOGW("Unable to write to ums lunfile %s (%s)\n", lun_file, strerror(errno)); - close(fd); - return -1; - } else { - // Volume path to LUN association succeeded - close(fd); - - // Save off a record of this lun_file being in use now - node = (struct lun_node *)malloc(sizeof(struct lun_node)); - node->lun_file = strdup(lun_file); - node->next = NULL; - if (lun_head == NULL) - lun_head = lun_tail = node; - else { - lun_tail->next = node; - lun_tail = node; - } - - LOGI("Successfully %sshared %s on LUN file %s\n", enable ? "" : "un", vol->blk_device, lun_file); - return 0; - } -} - -int control_usb_storage_for_lun(Volume* vol, bool enable) { - static const char* lun_files[] = { -#ifdef BOARD_UMS_LUNFILE - BOARD_UMS_LUNFILE, -#endif -#ifdef TARGET_USE_CUSTOM_LUN_FILE_PATH - TARGET_USE_CUSTOM_LUN_FILE_PATH, -#endif - "/sys/devices/platform/usb_mass_storage/lun%d/file", - "/sys/class/android_usb/android0/f_mass_storage/lun/file", - "/sys/class/android_usb/android0/f_mass_storage/lun_ex/file", - NULL - }; - - // If recovery.fstab specifies a LUN file, use it - if (vol->lun) { - return control_usb_storage_set_lun(vol, enable, vol->lun); - } - - // Try to find a LUN for this volume - // - iterate through the lun file paths - // - expand any %d by LUN_FILE_EXPANDS - int lun_num = 0; - int i; - for(i = 0; lun_files[i]; i++) { - const char *lun_file = lun_files[i]; - for(lun_num = 0; lun_num < LUN_FILE_EXPANDS; lun_num++) { - char formatted_lun_file[255]; - - // Replace %d with the LUN number - bzero(formatted_lun_file, 255); - snprintf(formatted_lun_file, 254, lun_file, lun_num); - - // Attempt to use the LUN file - if (control_usb_storage_set_lun(vol, enable, formatted_lun_file) == 0) { - return 0; +static int control_usb_storage(bool on) +{ + int i = 0; + int num = 0; + + for (i = 0; i < get_num_volumes(); i++) { + Volume *v = get_device_volumes() + i; + if (fs_mgr_is_voldmanaged(v) && vold_is_volume_available(v->mount_point)) { + if (on) { + vold_share_volume(v->mount_point); + } else { + vold_unshare_volume(v->mount_point, 1); } + num++; } } - - // All LUNs were exhausted and none worked - LOGW("Could not %sable %s on LUN %d\n", enable ? "en" : "dis", vol->blk_device, lun_num); - - return -1; // -1 failure, 0 success -} - -int control_usb_storage(Volume **volumes, bool enable) { - int res = -1; - int i; - for(i = 0; i < MAX_NUM_USB_VOLUMES; i++) { - Volume *volume = volumes[i]; - if (volume) { - int vol_res = control_usb_storage_for_lun(volume, enable); - if (vol_res == 0) res = 0; // if any one path succeeds, we return success - } - } - - // Release memory used by the LUN file linked list - struct lun_node *node = lun_head; - while(node) { - struct lun_node *next = node->next; - free((void *)node->lun_file); - free(node); - node = next; - } - lun_head = lun_tail = NULL; - - return res; // -1 failure, 0 success + return num; } void show_mount_usb_storage_menu() { - // Build a list of Volume objects; some or all may not be valid - Volume* volumes[MAX_NUM_USB_VOLUMES] = { - volume_for_path("/sdcard"), - volume_for_path("/emmc"), - volume_for_path("/external_sd") - }; - - // Enable USB storage - if (control_usb_storage(volumes, 1)) + // Enable USB storage using vold + if (!control_usb_storage(true)) return; - static char* headers[] = { "USB Mass Storage device", + static const char* headers[] = { "USB Mass Storage device", "Leaving this menu unmounts", "your SD card from your PC.", "", @@ -603,26 +504,29 @@ void show_mount_usb_storage_menu() } // Disable USB storage - control_usb_storage(volumes, 0); + control_usb_storage(false); } int confirm_selection(const char* title, const char* confirm) { struct stat info; + int ret = 0; + if (0 == stat("/sdcard/clockworkmod/.no_confirm", &info)) return 1; - char* confirm_headers[] = { title, " THIS CAN NOT BE UNDONE.", "", NULL }; + char* confirm_str = strdup(confirm); + const char* confirm_headers[] = { title, " THIS CAN NOT BE UNDONE.", "", NULL }; int one_confirm = 0 == stat("/sdcard/clockworkmod/.one_confirm", &info); #ifdef BOARD_TOUCH_RECOVERY one_confirm = 1; -#endif +#endif if (one_confirm) { char* items[] = { "No", - confirm, //" Yes -- wipe partition", // [1] + confirm_str, //" Yes -- wipe partition", // [1] NULL }; int chosen_item = get_menu_selection(confirm_headers, items, 0, 0); - return chosen_item == 1; + ret = (chosen_item == 1); } else { char* items[] = { "No", @@ -632,19 +536,22 @@ int confirm_selection(const char* title, const char* confirm) "No", "No", "No", - confirm, //" Yes -- wipe partition", // [7] + confirm_str, //" Yes -- wipe partition", // [7] "No", "No", "No", NULL }; int chosen_item = get_menu_selection(confirm_headers, items, 0, 0); - return chosen_item == 7; - } + ret = (chosen_item == 7); } + free(confirm_str); + return ret; +} #define MKE2FS_BIN "/sbin/mke2fs" #define TUNE2FS_BIN "/sbin/tune2fs" #define E2FSCK_BIN "/sbin/e2fsck" +extern void reset_ext4fs_info(); extern struct selabel_handle *sehandle; int format_device(const char *device, const char *path, const char *fs_type) { @@ -816,12 +723,12 @@ int format_unknown_device(const char *device, const char* path, const char *fs_t typedef struct { char mount[255]; char unmount[255]; - Volume* v; + char path[PATH_MAX]; } MountMenuEntry; typedef struct { char txt[255]; - Volume* v; + char path[PATH_MAX]; } FormatMenuEntry; int is_safe_to_format(char* name) @@ -841,29 +748,29 @@ int is_safe_to_format(char* name) return 1; } -void show_partition_menu() +int show_partition_menu() { - static char* headers[] = { "Mounts and Storage Menu", + static const char* headers[] = { "Mounts and Storage Menu", "", NULL }; + static char* confirm_format = "Confirm format?"; + static char* confirm = "Yes - Format"; + char confirm_string[255]; + static MountMenuEntry* mount_menu = NULL; static FormatMenuEntry* format_menu = NULL; - - typedef char* string; + static char* list[256]; int i, mountable_volumes, formatable_volumes; int num_volumes; - Volume* device_volumes; + int chosen_item = 0; num_volumes = get_num_volumes(); - device_volumes = get_device_volumes(); - string options[255]; - - if(!device_volumes) - return; + if(!num_volumes) + return 0; mountable_volumes = 0; formatable_volumes = 0; @@ -871,66 +778,62 @@ void show_partition_menu() mount_menu = malloc(num_volumes * sizeof(MountMenuEntry)); format_menu = malloc(num_volumes * sizeof(FormatMenuEntry)); - for (i = 0; i < num_volumes; ++i) { - Volume* v = &device_volumes[i]; + for (i = 0; i < num_volumes; i++) { + Volume* v = get_device_volumes() + i; - if (fs_mgr_is_voldmanaged(v)) continue; + if (fs_mgr_is_voldmanaged(v) && !vold_is_volume_available(v->mount_point)) { + continue; + } if(strcmp("ramdisk", v->fs_type) != 0 && strcmp("mtd", v->fs_type) != 0 && strcmp("emmc", v->fs_type) != 0 && strcmp("bml", v->fs_type) != 0) { if (strcmp("datamedia", v->fs_type) != 0) { - sprintf(&mount_menu[mountable_volumes].mount, "mount %s", v->mount_point); - sprintf(&mount_menu[mountable_volumes].unmount, "unmount %s", v->mount_point); - mount_menu[mountable_volumes].v = &device_volumes[i]; + sprintf(mount_menu[mountable_volumes].mount, "mount %s", v->mount_point); + sprintf(mount_menu[mountable_volumes].unmount, "unmount %s", v->mount_point); + sprintf(mount_menu[mountable_volumes].path, "%s", v->mount_point); ++mountable_volumes; } if (is_safe_to_format(v->mount_point)) { - sprintf(&format_menu[formatable_volumes].txt, "format %s", v->mount_point); - format_menu[formatable_volumes].v = &device_volumes[i]; + sprintf(format_menu[formatable_volumes].txt, "format %s", v->mount_point); + sprintf(format_menu[formatable_volumes].path, "%s", v->mount_point); ++formatable_volumes; } } else if (strcmp("ramdisk", v->fs_type) != 0 && strcmp("mtd", v->fs_type) == 0 && is_safe_to_format(v->mount_point)) { - sprintf(&format_menu[formatable_volumes].txt, "format %s", v->mount_point); - format_menu[formatable_volumes].v = &device_volumes[i]; + sprintf(format_menu[formatable_volumes].txt, "format %s", v->mount_point); + sprintf(format_menu[formatable_volumes].path, "%s", v->mount_point); ++formatable_volumes; } } - static char* confirm_format = "Confirm format?"; - static char* confirm = "Yes - Format"; - char confirm_string[255]; - for (;;) { for (i = 0; i < mountable_volumes; i++) { MountMenuEntry* e = &mount_menu[i]; - Volume* v = e->v; - if(is_path_mounted(v->mount_point)) - options[i] = e->unmount; + if(is_path_mounted(e->path)) + list[i] = e->unmount; else - options[i] = e->mount; + list[i] = e->mount; } for (i = 0; i < formatable_volumes; i++) { FormatMenuEntry* e = &format_menu[i]; - - options[mountable_volumes+i] = e->txt; + list[mountable_volumes+i] = e->txt; } if (!is_data_media()) { - options[mountable_volumes + formatable_volumes] = "mount USB storage"; - options[mountable_volumes + formatable_volumes + 1] = NULL; + list[mountable_volumes + formatable_volumes] = "mount USB storage"; + list[mountable_volumes + formatable_volumes + 1] = '\0'; } else { - options[mountable_volumes + formatable_volumes] = "format /data and /data/media (/sdcard)"; - options[mountable_volumes + formatable_volumes + 1] = "mount USB storage"; - options[mountable_volumes + formatable_volumes + 2] = NULL; + list[mountable_volumes + formatable_volumes] = "format /data and /data/media (/sdcard)"; + list[mountable_volumes + formatable_volumes + 1] = "mount USB storage"; + list[mountable_volumes + formatable_volumes + 2] = '\0'; } - int chosen_item = get_menu_selection(headers, &options, 0, 0); - if (chosen_item == GO_BACK) + chosen_item = get_menu_selection(headers, list, 0, 0); + if (chosen_item == GO_BACK || chosen_item == REFRESH) break; if (chosen_item == (mountable_volumes+formatable_volumes)) { if (!is_data_media()) { @@ -953,32 +856,30 @@ void show_partition_menu() } else if (chosen_item < mountable_volumes) { MountMenuEntry* e = &mount_menu[chosen_item]; - Volume* v = e->v; - if (is_path_mounted(v->mount_point)) + if (is_path_mounted(e->path)) { - if (0 != ensure_path_unmounted(v->mount_point)) - ui_print("Error unmounting %s!\n", v->mount_point); + if (0 != ensure_path_unmounted(e->path)) + ui_print("Error unmounting %s!\n", e->path); } else { - if (0 != ensure_path_mounted(v->mount_point)) - ui_print("Error mounting %s!\n", v->mount_point); + if (0 != ensure_path_mounted(e->path)) + ui_print("Error mounting %s!\n", e->path); } } else if (chosen_item < (mountable_volumes + formatable_volumes)) { chosen_item = chosen_item - mountable_volumes; FormatMenuEntry* e = &format_menu[chosen_item]; - Volume* v = e->v; - sprintf(confirm_string, "%s - %s", v->mount_point, confirm_format); + sprintf(confirm_string, "%s - %s", e->path, confirm_format); if (!confirm_selection(confirm_string, confirm)) continue; - ui_print("Formatting %s...\n", v->mount_point); - if (0 != format_volume(v->mount_point)) - ui_print("Error formatting %s!\n", v->mount_point); + ui_print("Formatting %s...\n", e->path); + if (0 != format_volume(e->path)) + ui_print("Error formatting %s!\n", e->path); else ui_print("Done.\n"); } @@ -986,6 +887,7 @@ void show_partition_menu() free(mount_menu); free(format_menu); + return chosen_item; } void show_nandroid_advanced_restore_menu(const char* path) @@ -995,7 +897,7 @@ void show_nandroid_advanced_restore_menu(const char* path) return; } - static char* advancedheaders[] = { "Choose an image to restore", + static const char* advancedheaders[] = { "Choose an image to restore", "", "Choose an image to restore", "first. The next menu will", @@ -1010,7 +912,7 @@ void show_nandroid_advanced_restore_menu(const char* path) if (file == NULL) return; - static char* headers[] = { "Advanced Restore", + static const char* headers[] = { "Advanced Restore", "", NULL }; @@ -1061,19 +963,28 @@ void show_nandroid_advanced_restore_menu(const char* path) } } -static void run_dedupe_gc(const char* other_sd) { - ensure_path_mounted("/sdcard"); - nandroid_dedupe_gc("/sdcard/clockworkmod/blobs"); - if (other_sd) { - ensure_path_mounted(other_sd); - char tmp[PATH_MAX]; - sprintf(tmp, "%s/clockworkmod/blobs", other_sd); - nandroid_dedupe_gc(tmp); +static void run_dedupe_gc() { + char path[PATH_MAX]; + char* fmt = "%s/clockworkmod/blobs"; + char* primary_path = get_primary_storage_path(); + char** extra_paths = get_extra_storage_paths(); + int i = 0; + + sprintf(path, fmt, primary_path); + ensure_path_mounted(primary_path); + nandroid_dedupe_gc(path); + + if (extra_paths != NULL) { + for (i = 0; i < get_num_extra_volumes(); i++) { + ensure_path_mounted(extra_paths[i]); + sprintf(path, fmt, extra_paths[i]); + nandroid_dedupe_gc(path); + } } } static void choose_default_backup_format() { - static char* headers[] = { "Default Backup Format", + static const char* headers[] = { "Default Backup Format", "", NULL }; @@ -1107,53 +1018,77 @@ static void choose_default_backup_format() { } } -void show_nandroid_menu() +static void add_nandroid_options_for_volume(char** menu, char* path, int offset) { - static char* headers[] = { "Backup and Restore", - "", - NULL - }; + char buf[100]; - char* list[] = { "backup", - "restore", - "delete", - "advanced restore", - "free unused backup data", - "choose default backup format", - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL + sprintf(buf, "backup to %s", path); + menu[offset] = strdup(buf); + + sprintf(buf, "restore from %s", path); + menu[offset + 1] = strdup(buf); + + sprintf(buf, "delete from %s", path); + menu[offset + 2] = strdup(buf); + + sprintf(buf, "advanced restore from %s", path); + menu[offset + 3] = strdup(buf); +} + +int show_nandroid_menu() +{ + char* primary_path = get_primary_storage_path(); + char** extra_paths = get_extra_storage_paths(); + int num_extra_volumes = get_num_extra_volumes(); + int i = 0, offset = 0, chosen_item = 0; + char* chosen_path = NULL; + int max_backup_index = (num_extra_volumes + 1) * 4; + + static const char* headers[] = { "Backup and Restore", + "", + NULL }; - char *other_sd = NULL; - if (volume_for_path("/emmc") != NULL) { - other_sd = "/emmc"; - list[6] = "backup to internal sdcard"; - list[7] = "restore from internal sdcard"; - list[8] = "advanced restore from internal sdcard"; - list[9] = "delete from internal sdcard"; - } - else if (volume_for_path("/external_sd") != NULL) { - other_sd = "/external_sd"; - list[6] = "backup to external sdcard"; - list[7] = "restore from external sdcard"; - list[8] = "advanced restore from external sdcard"; - list[9] = "delete from external sdcard"; + static char* list[((MAX_NUM_MANAGED_VOLUMES + 1) * 4) + 2]; + + add_nandroid_options_for_volume(list, primary_path, offset); + offset += 4; + + if (extra_paths != NULL) { + for (i = 0; i < num_extra_volumes; i++) { + add_nandroid_options_for_volume(list, extra_paths[i], offset); + offset += 4; + } } + + list[offset] = "free unused backup data"; + offset++; + list[offset] = "choose default backup format"; + offset++; + #ifdef RECOVERY_EXTEND_NANDROID_MENU - extend_nandroid_menu(list, 10, sizeof(list) / sizeof(char*)); + extend_nandroid_menu(list, offset, sizeof(list) / sizeof(char*)); + offset++; #endif + list[offset] = NULL; + for (;;) { - int chosen_item = get_filtered_menu_selection(headers, list, 0, 0, sizeof(list) / sizeof(char*)); - if (chosen_item == GO_BACK) + chosen_item = get_filtered_menu_selection(headers, list, 0, 0, offset); + if (chosen_item == GO_BACK || chosen_item == REFRESH) break; - switch (chosen_item) - { + int chosen_subitem = chosen_item % 4; + if (chosen_item == max_backup_index) { + run_dedupe_gc(); + } else if (chosen_item == (max_backup_index + 1)) { + choose_default_backup_format(); + } else if (chosen_item < max_backup_index){ + if (chosen_item < 4) { + chosen_path = primary_path; + } else if (extra_paths != NULL) { + chosen_path = extra_paths[(chosen_item / 4) -1]; + } + switch (chosen_subitem) { case 0: { char backup_path[PATH_MAX]; @@ -1163,88 +1098,46 @@ void show_nandroid_menu() { struct timeval tp; gettimeofday(&tp, NULL); - sprintf(backup_path, "/sdcard/clockworkmod/backup/%d", tp.tv_sec); + sprintf(backup_path, "%s/clockworkmod/backup/%ld", chosen_path, tp.tv_sec); } else { - strftime(backup_path, sizeof(backup_path), "/sdcard/clockworkmod/backup/%F.%H.%M.%S", tmp); + char path_fmt[PATH_MAX]; + strftime(path_fmt, sizeof(path_fmt), "clockworkmod/backup/%F.%H.%M.%S", tmp); + // this sprintf results in: + // /emmc/clockworkmod/backup/%F.%H.%M.%S (time values are populated too) + sprintf(backup_path, "%s/%s", chosen_path, path_fmt); } nandroid_backup(backup_path); write_recovery_version(); } break; case 1: - show_nandroid_restore_menu("/sdcard"); + show_nandroid_restore_menu(chosen_path); write_recovery_version(); break; case 2: - show_nandroid_delete_menu("/sdcard"); + show_nandroid_delete_menu(chosen_path); write_recovery_version(); break; case 3: - show_nandroid_advanced_restore_menu("/sdcard"); + show_nandroid_advanced_restore_menu(chosen_path); write_recovery_version(); break; - case 4: - run_dedupe_gc(other_sd); - break; - case 5: - choose_default_backup_format(); - break; - case 6: - { - char backup_path[PATH_MAX]; - time_t t = time(NULL); - struct tm *timeptr = localtime(&t); - if (timeptr == NULL) - { - struct timeval tp; - gettimeofday(&tp, NULL); - if (other_sd != NULL) { - sprintf(backup_path, "%s/clockworkmod/backup/%d", other_sd, tp.tv_sec); - } - else { - break; - } - } - else - { - if (other_sd != NULL) { - char tmp[PATH_MAX]; - strftime(tmp, sizeof(tmp), "clockworkmod/backup/%F.%H.%M.%S", timeptr); - // this sprintf results in: - // /emmc/clockworkmod/backup/%F.%H.%M.%S (time values are populated too) - sprintf(backup_path, "%s/%s", other_sd, tmp); - } - else { - break; - } - } - nandroid_backup(backup_path); - } - break; - case 7: - if (other_sd != NULL) { - show_nandroid_restore_menu(other_sd); - } - break; - case 8: - if (other_sd != NULL) { - show_nandroid_advanced_restore_menu(other_sd); - } - break; - case 9: - if (other_sd != NULL) { - show_nandroid_delete_menu(other_sd); - } - break; default: + break; + } + } else { #ifdef RECOVERY_EXTEND_NANDROID_MENU handle_nandroid_menu(10, chosen_item); #endif - break; + goto out; } } +out: + for (i = 0; i < max_backup_index; i++) + free(list[i]); + return chosen_item; } static void partition_sdcard(const char* volume) { @@ -1273,9 +1166,9 @@ static void partition_sdcard(const char* volume) { NULL }; - static char* ext_headers[] = { "Ext Size", "", NULL }; - static char* swap_headers[] = { "Swap Size", "", NULL }; - static char* fstype_headers[] = {"Partition Type", "", NULL }; + static const char* ext_headers[] = { "Ext Size", "", NULL }; + static const char* swap_headers[] = { "Swap Size", "", NULL }; + static const char* fstype_headers[] = {"Partition Type", "", NULL }; int ext_size = get_menu_selection(ext_headers, ext_sizes, 0, 0); if (ext_size == GO_BACK) @@ -1293,7 +1186,7 @@ static void partition_sdcard(const char* volume) { Volume *vol = volume_for_path(volume); strcpy(sddevice, vol->blk_device); // we only want the mmcblk, not the partition - sddevice[strlen("/dev/block/mmcblkX")] = NULL; + sddevice[strlen("/dev/block/mmcblkX")] = '\0'; char cmd[PATH_MAX]; setenv("SDPATH", sddevice, 1); sprintf(cmd, "sdparted -es %s -ss %s -efs %s -s", ext_sizes[ext_size], swap_sizes[swap_size], partition_types[partition_type]); @@ -1305,6 +1198,9 @@ static void partition_sdcard(const char* volume) { } int can_partition(const char* volume) { + if (is_data_media_volume_path(volume)) + return 0; + Volume *vol = volume_for_path(volume); if (vol == NULL) { LOGI("Can't format unknown volume: %s\n", volume); @@ -1326,46 +1222,60 @@ int can_partition(const char* volume) { return 1; } -void show_advanced_menu() + +int show_advanced_menu() { - static char* headers[] = { "Advanced Menu", + char buf[80]; + int i = 0, j = 0, chosen_item = 0; + static char* list[MAX_NUM_MANAGED_VOLUMES + 9]; + + char* primary_path = get_primary_storage_path(); + char** extra_paths = get_extra_storage_paths(); + int num_extra_volumes = get_num_extra_volumes(); + + static const char* headers[] = { "Advanced Menu", "", NULL }; - static char* list[] = { "reboot recovery", - "reboot to bootloader", - "power off", - "wipe dalvik cache", - "report error", - "key test", - "show log", - "partition sdcard", - "partition external sdcard", - "partition internal sdcard", - NULL - }; + memset(list, 0, MAX_NUM_MANAGED_VOLUMES + 9); + + list[0] = "reboot recovery"; char bootloader_mode[PROPERTY_VALUE_MAX]; property_get("ro.bootloader.mode", bootloader_mode, ""); if (!strcmp(bootloader_mode, "download")) { list[1] = "reboot to download mode"; + } else { + list[1] = "reboot to bootloader"; } - if (!can_partition("/sdcard")) { - list[7] = NULL; - } - if (!can_partition("/external_sd")) { - list[8] = NULL; + list[2] = "power off"; + list[3] = "wipe dalvik cache"; + list[4] = "report error"; + list[5] = "key test"; + list[6] = "show log"; + + if (can_partition(primary_path)) { + sprintf(buf, "partition %s", primary_path); + list[7] = strdup(buf); + j++; } - if (!can_partition("/emmc")) { - list[9] = NULL; + + if (extra_paths != NULL) { + for (i = 0; i < num_extra_volumes; i++) { + if (can_partition(extra_paths[i])) { + sprintf(buf, "partition %s", extra_paths[i]); + list[7 + j] = strdup(buf); + j++; + } + } } for (;;) { - int chosen_item = get_filtered_menu_selection(headers, list, 0, 0, sizeof(list) / sizeof(char*)); - if (chosen_item == GO_BACK) + chosen_item = get_filtered_menu_selection(headers, list, 0, 0, sizeof(list) / sizeof(char*)); + if (chosen_item == GO_BACK || chosen_item == REFRESH) break; switch (chosen_item) { @@ -1427,16 +1337,21 @@ void show_advanced_menu() ui_printlogtail(12); break; case 7: - partition_sdcard("/sdcard"); - break; - case 8: - partition_sdcard("/external_sd"); + partition_sdcard(primary_path); break; - case 9: - partition_sdcard("/emmc"); + default: + if (chosen_item >= 8) { + partition_sdcard(list[chosen_item] + 10); + } break; } } + free(list[7]); + if (extra_paths != NULL) { + for (; j >= 0; --j) + free(list[8 + j]); + } + return chosen_item; } void write_fstab_root(char *path, FILE *file) @@ -1536,8 +1451,8 @@ void process_volumes() { char backup_name[PATH_MAX]; struct timeval tp; gettimeofday(&tp, NULL); - sprintf(backup_name, "before-ext4-convert-%d", tp.tv_sec); - sprintf(backup_path, "/sdcard/clockworkmod/backup/%s", backup_name); + sprintf(backup_name, "before-ext4-convert-%ld", tp.tv_sec); + sprintf(backup_path, "%s/clockworkmod/backup/%s", get_primary_storage_path(), backup_name); ui_set_show_text(1); ui_print("Filesystems need to be converted to ext4.\n"); @@ -1555,14 +1470,14 @@ void handle_failure(int ret) { if (ret == 0) return; - if (0 != ensure_path_mounted("/sdcard")) + if (0 != ensure_path_mounted(get_primary_storage_path())) return; mkdir("/sdcard/clockworkmod", S_IRWXU | S_IRWXG | S_IRWXO); __system("cp /tmp/recovery.log /sdcard/clockworkmod/recovery.log"); ui_print("/tmp/recovery.log was copied to /sdcard/clockworkmod/recovery.log. Please open ROM Manager to report the issue.\n"); } -int is_path_mounted(const char* path) { +static int is_path_mounted(const char* path) { Volume* v = volume_for_path(path); if (v == NULL) { return 0; @@ -1572,12 +1487,8 @@ int is_path_mounted(const char* path) { return 1; } - int result; - result = scan_mounted_volumes(); - if (result < 0) { - LOGE("failed to scan mounted volumes\n"); + if (scan_mounted_volumes() < 0) return 0; - } const MountedVolume* mv = find_mounted_volume_by_mount_point(v->mount_point); diff --git a/extendedcommands.h b/extendedcommands.h index f6782f768..5919af5b2 100644 --- a/extendedcommands.h +++ b/extendedcommands.h @@ -22,22 +22,19 @@ show_nandroid_restore_menu(const char* path); void show_nandroid_advanced_restore_menu(const char* path); -void +int show_nandroid_menu(); -void +int show_partition_menu(); -void -show_choose_zip_menu(); - int install_zip(const char* packagefilepath); int __system(const char *command); -void +int show_advanced_menu(); int format_unknown_device(const char *device, const char* path, const char *fs_type); @@ -55,7 +52,7 @@ void process_volumes(); int extendedcommand_file_exists(); -void show_install_update_menu(); +int show_install_update_menu(); int confirm_selection(const char* title, const char* confirm); @@ -63,7 +60,13 @@ int run_and_remove_extendedcommand(); int verify_root_and_recovery(); +void free_string_array(char** array); + +int can_partition(const char* volume); + +static int is_path_mounted(const char* path); + #ifdef RECOVERY_EXTEND_NANDROID_MENU void extend_nandroid_menu(char** items, int item_count, int max_items); void handle_nandroid_menu(int item_count, int selected); -#endif \ No newline at end of file +#endif diff --git a/minui/Android.mk b/minui/Android.mk index 7a327f5b4..500359f7b 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -35,4 +35,10 @@ ifneq ($(BOARD_USE_CUSTOM_RECOVERY_FONT),) LOCAL_CFLAGS += -DBOARD_USE_CUSTOM_RECOVERY_FONT=$(BOARD_USE_CUSTOM_RECOVERY_FONT) endif +# Some devices need kernel headers for graphics +ifeq ($(TARGET_PREBUILT_KERNEL),) + LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr + LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include +endif + include $(BUILD_STATIC_LIBRARY) diff --git a/mounts.c b/mounts.c index e8b69e832..5dc5a1749 100644 --- a/mounts.c +++ b/mounts.c @@ -106,10 +106,10 @@ scan_mounted_volumes() */ bufp = buf; while (nbytes > 0) { - char device[64]; - char mount_point[64]; + char device[PATH_MAX]; + char mount_point[PATH_MAX]; char filesystem[64]; - char flags[128]; + char flags[256]; int matches; /* %as is a gnu extension that malloc()s a string for each field. diff --git a/nandroid.c b/nandroid.c index 2aec63c0a..68c4bc7e4 100644 --- a/nandroid.c +++ b/nandroid.c @@ -211,7 +211,7 @@ static void refresh_default_backup_handler() { strcpy(fmt, forced_backup_format); } else { - ensure_path_mounted("/sdcard"); + ensure_path_mounted(get_primary_storage_path()); FILE* f = fopen(NANDROID_BACKUP_FORMAT_FILE, "r"); if (NULL == f) { default_backup_handler = tar_compress_wrapper; @@ -267,6 +267,7 @@ int nandroid_backup_partition_extended(const char* backup_path, const char* moun int ret = 0; char name[PATH_MAX]; + char tmp[PATH_MAX]; strcpy(name, basename(mount_point)); struct stat file_info; @@ -278,7 +279,6 @@ int nandroid_backup_partition_extended(const char* backup_path, const char* moun return ret; } compute_directory_stats(mount_point); - char tmp[PATH_MAX]; scan_mounted_volumes(); Volume *v = volume_for_path(mount_point); MountedVolume *mv = NULL; @@ -404,11 +404,11 @@ int nandroid_backup(const char* backup_path) return ret; } - if (is_data_media() || 0 != stat("/sdcard/.android_secure", &s)) { - ui_print("No /sdcard/.android_secure found. Skipping backup of applications on external storage.\n"); + if (is_data_media() || 0 != stat(get_android_secure_path(), &s)) { + ui_print("No .android_secure found. Skipping backup of applications on external storage.\n"); } else { - if (0 != (ret = nandroid_backup_partition_extended(backup_path, "/sdcard/.android_secure", 0))) + if (0 != (ret = nandroid_backup_partition_extended(backup_path, get_android_secure_path(), 0))) return ret; } @@ -793,7 +793,7 @@ int nandroid_restore(const char* backup_path, int restore_boot, int restore_syst return ret; } - if (restore_data && 0 != (ret = nandroid_restore_partition_extended(backup_path, "/sdcard/.android_secure", 0))) + if (restore_data && 0 != (ret = nandroid_restore_partition_extended(backup_path, get_android_secure_path(), 0))) return ret; if (restore_cache && 0 != (ret = nandroid_restore_partition_extended(backup_path, "/cache", 0))) diff --git a/recovery.c b/recovery.c index 094b90891..e5b3e471f 100644 --- a/recovery.c +++ b/recovery.c @@ -41,6 +41,8 @@ #include "roots.h" #include "recovery_ui.h" +#include "voldclient/voldclient.h" + #include "adb_install.h" #include "minadbd/adb.h" @@ -455,7 +457,7 @@ prepend_title(char** headers) { } int -get_menu_selection(char** headers, char** items, int menu_only, +get_menu_selection(const char** headers, char** items, int menu_only, int initial_selection) { // throw away keys pressed previously, so user doesn't // accidentally trigger menu items. @@ -465,7 +467,7 @@ get_menu_selection(char** headers, char** items, int menu_only, int selected = initial_selection; int chosen_item = -1; - while (chosen_item < 0 && chosen_item != GO_BACK) { + while (chosen_item < 0 && chosen_item != GO_BACK && chosen_item != REFRESH) { int key = ui_wait_key(); int visible = ui_text_visible(); @@ -481,6 +483,9 @@ get_menu_selection(char** headers, char** items, int menu_only, else if (key == -2) { return GO_BACK; } + else if (key == -6) { + return REFRESH; + } int action = ui_handle_key(key, visible); @@ -510,6 +515,9 @@ get_menu_selection(char** headers, char** items, int menu_only, case GO_BACK: chosen_item = GO_BACK; break; + case REFRESH: + chosen_item = REFRESH; + break; } } else if (!menu_only) { chosen_item = action; @@ -665,7 +673,7 @@ wipe_data(int confirm) { erase_volume("/datadata"); } erase_volume("/sd-ext"); - erase_volume("/sdcard/.android_secure"); + erase_volume(get_android_secure_path()); ui_print("Data wipe complete.\n"); } @@ -703,41 +711,50 @@ prompt_and_wait() { chosen_item = device_perform_action(chosen_item); int status; - switch (chosen_item) { - case ITEM_REBOOT: - poweroff = 0; - return; - - case ITEM_WIPE_DATA: - wipe_data(ui_text_visible()); - if (!ui_text_visible()) return; - break; - - case ITEM_WIPE_CACHE: - if (confirm_selection("Confirm wipe?", "Yes - Wipe Cache")) - { - ui_print("\n-- Wiping cache...\n"); - erase_volume("/cache"); - ui_print("Cache wipe complete.\n"); + int ret = 0; + + for (;;) { + switch (chosen_item) { + case ITEM_REBOOT: + poweroff = 0; + return; + + case ITEM_WIPE_DATA: + wipe_data(ui_text_visible()); if (!ui_text_visible()) return; - } - break; + break; + + case ITEM_WIPE_CACHE: + if (confirm_selection("Confirm wipe?", "Yes - Wipe Cache")) + { + ui_print("\n-- Wiping cache...\n"); + erase_volume("/cache"); + ui_print("Cache wipe complete.\n"); + if (!ui_text_visible()) return; + } + break; - case ITEM_APPLY_ZIP: - show_install_update_menu(); - break; + case ITEM_APPLY_ZIP: + ret = show_install_update_menu(); + break; - case ITEM_NANDROID: - show_nandroid_menu(); - break; + case ITEM_NANDROID: + ret = show_nandroid_menu(); + break; - case ITEM_PARTITION: - show_partition_menu(); - break; + case ITEM_PARTITION: + ret = show_partition_menu(); + break; - case ITEM_ADVANCED: - show_advanced_menu(); - break; + case ITEM_ADVANCED: + ret = show_advanced_menu(); + break; + } + if (ret == REFRESH) { + ret = 0; + continue; + } + break; } } } @@ -786,9 +803,39 @@ setup_adbd() { void reboot_main_system(int cmd, int flags, char *arg) { verify_root_and_recovery(); finish_recovery(NULL); // sync() in here + vold_unmount_all(); android_reboot(cmd, flags, arg); } +static int v_changed = 0; +int volumes_changed() { + int ret = v_changed; + if (v_changed == 1) + v_changed = 0; + return ret; +} + +static int handle_volume_hotswap(char* label, char* path) { + v_changed = 1; + return 0; +} + +static int handle_volume_state_changed(char* label, char* path, int state) { + if (state == State_Checking || + state == State_Mounted || + state == State_Idle || + state == State_Formatting || + state == State_Shared) + ui_print("%s: %s\n", path, stateToStr(state)); + return 0; +} + +static struct vold_callbacks v_callbacks = { + .state_changed = handle_volume_state_changed, + .disk_added = handle_volume_hotswap, + .disk_removed = handle_volume_hotswap, +}; + int main(int argc, char **argv) { @@ -848,6 +895,22 @@ main(int argc, char **argv) { setup_adbd(); return 0; } + if (strstr(argv[0], "start")) { + property_set("ctl.start", argv[1]); + return 0; + } + if (strstr(argv[0], "stop")) { + property_set("ctl.stop", argv[1]); + return 0; + } + if (strstr(argv[0], "fsck_msdos")) + return fsck_msdos_main(argc, argv); + if (strstr(argv[0], "newfs_msdos")) + return newfs_msdos_main(argc, argv); + if (strstr(argv[0], "minivold")) + return vold_main(argc, argv); + if (strstr(argv[0], "vdc")) + return vdc_main(argc, argv, true); return busybox_driver(argc, argv); } __system("/sbin/postrecoveryboot.sh"); @@ -865,6 +928,8 @@ main(int argc, char **argv) { ui_print(EXPAND(RECOVERY_VERSION)"\n"); load_volume_table(); process_volumes(); + vold_client_start(&v_callbacks, 1); + setup_legacy_storage_paths(); LOGI("Processing arguments.\n"); ensure_path_mounted(LAST_LOG_FILE); rotate_last_logs(5); @@ -1013,6 +1078,8 @@ main(int argc, char **argv) { // Otherwise, get ready to boot the main system... finish_recovery(send_intent); + vold_unmount_all(); + sync(); if(!poweroff) { ui_print("Rebooting...\n"); diff --git a/recovery_ui.h b/recovery_ui.h index 0555122bc..0e1022a30 100644 --- a/recovery_ui.h +++ b/recovery_ui.h @@ -71,6 +71,7 @@ int device_wipe_data(); #define HIGHLIGHT_DOWN -3 #define SELECT_ITEM -4 #define GO_BACK -5 +#define REFRESH -6 #define ITEM_REBOOT 0 #define ITEM_APPLY_EXT 1 @@ -94,7 +95,7 @@ extern char* MENU_ITEMS[]; extern int ui_root_menu; int -get_menu_selection(char** headers, char** items, int menu_only, int initial_selection); +get_menu_selection(const char** headers, char** items, int menu_only, int initial_selection); void set_sdcard_update_bootloader_message(); diff --git a/roots.c b/roots.c index 45b1c8a7d..a4b41d138 100644 --- a/roots.c +++ b/roots.c @@ -33,6 +33,8 @@ #include "flashutils/flashutils.h" #include "extendedcommands.h" +#include "voldclient/voldclient.h" + static struct fstab *fstab = NULL; int get_num_volumes() { @@ -75,6 +77,67 @@ Volume* volume_for_path(const char* path) { return fs_mgr_get_entry_for_mount_point(fstab, path); } +int is_primary_storage_voldmanaged() { + Volume* v; + v = volume_for_path("/storage/sdcard0"); + return fs_mgr_is_voldmanaged(v); +} + +static char* primary_storage_path = NULL; +char* get_primary_storage_path() { + if (primary_storage_path == NULL) { + if (volume_for_path("/storage/sdcard0")) + primary_storage_path = "/storage/sdcard0"; + else + primary_storage_path = "/sdcard"; + } + return primary_storage_path; +} + +int get_num_extra_volumes() { + int num = 0; + int i; + for (i = 0; i < get_num_volumes(); i++) { + Volume* v = get_device_volumes() + i; + if ((strcmp("/external_sd", v->mount_point) == 0) || + ((strcmp(get_primary_storage_path(), v->mount_point) != 0) && + fs_mgr_is_voldmanaged(v) && vold_is_volume_available(v->mount_point))) + num++; + } + return num; +} + +char** get_extra_storage_paths() { + int i = 0, j = 0; + static char* paths[MAX_NUM_MANAGED_VOLUMES]; + int num_extra_volumes = get_num_extra_volumes(); + + if (num_extra_volumes == 0) + return NULL; + + for (i = 0; i < get_num_volumes(); i++) { + Volume* v = get_device_volumes() + i; + if ((strcmp("/external_sd", v->mount_point) == 0) || + ((strcmp(get_primary_storage_path(), v->mount_point) != 0) && + fs_mgr_is_voldmanaged(v) && vold_is_volume_available(v->mount_point))) { + paths[j] = v->mount_point; + j++; + } + } + paths[j] = NULL; + + return paths; +} + +static char* android_secure_path = NULL; +char* get_android_secure_path() { + if (android_secure_path == NULL) { + android_secure_path = malloc((17 + strlen(get_primary_storage_path())) * sizeof(char *)); + sprintf(android_secure_path, "%s/.android_secure", primary_storage_path); + } + return android_secure_path; +} + int try_mount(const char* device, const char* mount_point, const char* fs_type, const char* fs_options) { if (device == NULL || mount_point == NULL || fs_type == NULL) return -1; @@ -103,6 +166,9 @@ int is_data_media() { return 1; if (strcmp(vol->mount_point, "/sdcard") == 0) has_sdcard = 1; + if (fs_mgr_is_voldmanaged(vol) && + (strcmp(vol->mount_point, "/storage/sdcard0") == 0)) + has_sdcard = 1; } return !has_sdcard; } @@ -178,7 +244,10 @@ int ensure_path_mounted_at_mount_point(const char* path, const char* mount_point mkdir(mount_point, 0755); // in case it doesn't already exist - if (strcmp(v->fs_type, "yaffs2") == 0) { + if (fs_mgr_is_voldmanaged(v)) { + return vold_mount_volume(mount_point, 1) == CommandOkay ? 0 : -1; + + } else if (strcmp(v->fs_type, "yaffs2") == 0) { // mount an MTD partition as a YAFFS2 filesystem. mtd_scan_partitions(); const MtdPartition* partition; @@ -246,6 +315,9 @@ int ensure_path_unmounted(const char* path) { return 0; } + if (fs_mgr_is_voldmanaged(volume_for_path(v->mount_point))) + return vold_unmount_volume(v->mount_point, 0, 1) == CommandOkay ? 0 : -1; + return unmount_mounted_volume(mv); } @@ -268,6 +340,13 @@ int format_volume(const char* volume) { } } + if (fs_mgr_is_voldmanaged(v)) { + if (ensure_path_unmounted(volume) != 0) { + LOGE("format_volume failed to unmount %s", v->mount_point); + } + return vold_format_volume(v->mount_point, 1) == CommandOkay ? 0 : -1; + } + if (is_data_media_volume_path(volume)) { return format_unknown_device(NULL, volume, NULL); } @@ -336,3 +415,12 @@ int format_volume(const char* volume) { void ignore_data_media_workaround(int ignore) { ignore_data_media = ignore; } + +void setup_legacy_storage_paths() { + char* primary_path = get_primary_storage_path(); + + if (!is_data_media_volume_path(primary_path)) { + rmdir("/sdcard"); + symlink(primary_path, "/sdcard"); + } +} diff --git a/roots.h b/roots.h index 4b139bf8f..a14ff0e69 100644 --- a/roots.h +++ b/roots.h @@ -39,6 +39,11 @@ int ensure_path_unmounted(const char* path); // it is mounted. int format_volume(const char* volume); +char* get_primary_storage_path(); +char** get_extra_storage_paths(); +char* get_android_secure_path(); +void setup_legacy_storage_paths(); +int get_num_extra_volumes(); int get_num_volumes(); Volume* get_device_volumes(); @@ -48,4 +53,6 @@ void setup_data_media(); int is_data_media_volume_path(const char* path); void ignore_data_media_workaround(int ignore); +#define MAX_NUM_MANAGED_VOLUMES 10 + #endif // RECOVERY_ROOTS_H_ diff --git a/ui.c b/ui.c index 20f5223ea..6fef6cb03 100644 --- a/ui.c +++ b/ui.c @@ -33,6 +33,7 @@ #include #include "minui/minui.h" #include "recovery_ui.h" +#include "voldclient/voldclient.h" extern int __system(const char *command); @@ -466,6 +467,7 @@ static int input_callback(int fd, short revents, void *data) } if (ev.value > 0 && device_reboot_now(key_pressed, ev.code)) { + vold_unmount_all(); android_reboot(ANDROID_RB_RESTART, 0, 0); } @@ -866,27 +868,35 @@ void ui_cancel_wait_key() { pthread_mutex_unlock(&key_queue_mutex); } +extern int volumes_changed(); + int ui_wait_key() { if (boardEnableKeyRepeat) return ui_wait_key_with_repeat(); pthread_mutex_lock(&key_queue_mutex); + int timeouts = UI_WAIT_KEY_TIMEOUT_SEC; - // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is - // plugged in. + // Time out after 1 second to catch volume changes, and loop for + // UI_WAIT_KEY_TIMEOUT_SEC to restart a device not connected to USB do { struct timeval now; struct timespec timeout; gettimeofday(&now, NULL); timeout.tv_sec = now.tv_sec; timeout.tv_nsec = now.tv_usec * 1000; - timeout.tv_sec += UI_WAIT_KEY_TIMEOUT_SEC; + timeout.tv_sec += 1; int rc = 0; while (key_queue_len == 0 && rc != ETIMEDOUT) { rc = pthread_cond_timedwait(&key_queue_cond, &key_queue_mutex, &timeout); + if (volumes_changed()) { + pthread_mutex_unlock(&key_queue_mutex); + return REFRESH; + } } - } while (usb_connected() && key_queue_len == 0); + timeouts--; + } while ((timeouts || usb_connected()) && key_queue_len == 0); int key = -1; if (key_queue_len > 0) { @@ -930,7 +940,7 @@ int ui_wait_key_with_repeat() &timeout); } pthread_mutex_unlock(&key_queue_mutex); - if (rc == ETIMEDOUT && !usb_connected()) { + if (rc == ETIMEDOUT && !usb_connected() && !volumes_changed()) { return -1; } diff --git a/voldclient/Android.mk b/voldclient/Android.mk new file mode 100644 index 000000000..b76d7bea3 --- /dev/null +++ b/voldclient/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := libvoldclient +LOCAL_SRC_FILES := commands.c dispatcher.c event_loop.c +LOCAL_CFLAGS := -DMINIVOLD -Werror +LOCAL_C_INCLUDES := \ + bootable/recovery \ + system/core/fs_mgr/include \ + system/core/include \ + system/core/libcutils \ + system/vold +LOCAL_MODULE_TAGS := optional +include $(BUILD_STATIC_LIBRARY) diff --git a/voldclient/commands.c b/voldclient/commands.c new file mode 100644 index 000000000..7cbb20be2 --- /dev/null +++ b/voldclient/commands.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2013 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "voldclient.h" + +#include +#include +#include +#include +#include +#include + +#include "common.h" + +int vold_update_volumes() { + + const char *cmd[2] = {"volume", "list"}; + return vold_command(2, cmd, 1); +} + +int vold_mount_volume(const char* path, int wait) { + + const char *cmd[3] = { "volume", "mount", path }; + int state = vold_get_volume_state(path); + + if (state == State_Mounted) { + LOGI("Volume %s already mounted\n", path); + return 0; + } + + if (state != State_Idle) { + LOGI("Volume %s is not idle, current state is %d\n", path, state); + return -1; + } + + if (access(path, R_OK) != 0) { + mkdir(path, 0000); + chown(path, 1000, 1000); + } + return vold_command(3, cmd, wait); +} + +int vold_unmount_volume(const char* path, int force, int wait) { + + const char *cmd[4] = { "volume", "unmount", path, "force" }; + int state = vold_get_volume_state(path); + + if (state <= State_Idle) { + LOGI("Volume %s is not mounted", path); + return 0; + } + + if (state != State_Mounted) { + LOGI("Volume %s cannot be unmounted in state %d\n", path, state); + return -1; + } + + return vold_command(force ? 4: 3, cmd, wait); +} + +int vold_share_volume(const char* path) { + + const char *cmd[4] = { "volume", "share", path, "ums" }; + int state = vold_get_volume_state(path); + + if (state == State_Mounted) + vold_unmount_volume(path, 0, 1); + + return vold_command(4, cmd, 1); +} + +int vold_unshare_volume(const char* path, int mount) { + + const char *cmd[4] = { "volume", "unshare", path, "ums" }; + int state = vold_get_volume_state(path); + int ret = 0; + + if (state != State_Shared) { + LOGE("Volume %s is not shared - state=%d", path, state); + return 0; + } + + ret = vold_command(4, cmd, 1); + + if (mount) + vold_mount_volume(path, 1); + + return ret; +} + +int vold_format_volume(const char* path, int wait) { + + const char* cmd[3] = { "volume", "format", path }; + return vold_command(3, cmd, wait); +} diff --git a/voldclient/dispatcher.c b/voldclient/dispatcher.c new file mode 100644 index 000000000..1bfe96fd2 --- /dev/null +++ b/voldclient/dispatcher.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2013 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "voldclient.h" + +#include +#include +#include +#include +#include +#include + +#include "ResponseCode.h" + +#include "common.h" + +static struct vold_callbacks* callbacks = NULL; +static int should_automount = 0; + +struct volume_node { + const char *label; + const char *path; + int state; + struct volume_node *next; +}; + +static struct volume_node *volume_head = NULL; +static struct volume_node *volume_tail = NULL; + +static int num_volumes = 0; + +static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; + +void vold_set_callbacks(struct vold_callbacks* ev_callbacks) { + callbacks = ev_callbacks; +} + +void vold_set_automount(int automount) { + should_automount = automount; +} + +void vold_mount_all() { + + struct volume_node *node; + + pthread_rwlock_rdlock(&rwlock); + for (node = volume_head; node; node = node->next) { + if (node->state == State_Idle) { + vold_mount_volume(node->path, 0); + } + } + pthread_rwlock_unlock(&rwlock); +} + +void vold_unmount_all() { + + struct volume_node *node; + + pthread_rwlock_rdlock(&rwlock); + for (node = volume_head; node; node = node->next) { + if (node->state >= State_Shared) { + vold_unshare_volume(node->path, 0); + } + if (node->state == State_Mounted) { + vold_unmount_volume(node->path, 1, 1); + } + } + pthread_rwlock_unlock(&rwlock); +} + +int vold_get_volume_state(const char *path) { + + int ret = 0; + struct volume_node *node; + + pthread_rwlock_rdlock(&rwlock); + for (node = volume_head; node; node = node->next) { + if (strcmp(path, node->path) == 0) { + ret = node->state; + break; + } + } + pthread_rwlock_unlock(&rwlock); + return ret; +} + +int vold_get_num_volumes() { + return num_volumes; +} + +int vold_is_volume_available(const char *path) { + return vold_get_volume_state(path) > 0; +} + +static void free_volume_list_locked() { + + struct volume_node *node; + + node = volume_head; + while (node) { + struct volume_node *next = node->next; + free((void *)node->path); + free((void *)node->label); + free(node); + node = next; + } + volume_head = volume_tail = NULL; +} + +static int is_listing_volumes = 0; + +static void vold_handle_volume_list(const char* label, const char* path, int state) { + + struct volume_node *node; + + pthread_rwlock_wrlock(&rwlock); + if (is_listing_volumes == 0) { + free_volume_list_locked(); + num_volumes = 0; + is_listing_volumes = 1; + } + + node = (struct volume_node *)malloc(sizeof(struct volume_node)); + node->label = strdup(label); + node->path = strdup(path); + node->state = state; + node->next = NULL; + + if (volume_head == NULL) + volume_head = volume_tail = node; + else { + volume_tail->next = node; + volume_tail = node; + } + + num_volumes++; + pthread_rwlock_unlock(&rwlock); +} + +static void vold_handle_volume_list_done() { + + pthread_rwlock_wrlock(&rwlock); + is_listing_volumes = 0; + pthread_rwlock_unlock(&rwlock); +} + +static void set_volume_state(char* path, int state) { + + struct volume_node *node; + + pthread_rwlock_rdlock(&rwlock); + for (node = volume_head; node; node = node->next) { + if (strcmp(node->path, path) == 0) { + node->state = state; + break; + } + } + pthread_rwlock_unlock(&rwlock); +} + +static void vold_handle_volume_state_change(char* label, char* path, int state) { + + set_volume_state(path, state); + + if (callbacks != NULL && callbacks->state_changed != NULL) + callbacks->state_changed(label, path, state); +} + +static void vold_handle_volume_inserted(char* label, char* path) { + + set_volume_state(path, State_Idle); + + if (callbacks != NULL && callbacks->disk_added != NULL) + callbacks->disk_added(label, path); + + if (should_automount) + vold_mount_volume(path, 0); +} + +static void vold_handle_volume_removed(char* label, char* path) { + + set_volume_state(path, State_NoMedia); + + if (callbacks != NULL && callbacks->disk_removed != NULL) + callbacks->disk_removed(label, path); +} + +int vold_dispatch(int code, char** tokens, int len) { + + int i = 0; + int ret = 0; + + if (code == VolumeListResult) { + //