diff options
-rw-r--r-- | adb_install.cpp | 126 | ||||
-rw-r--r-- | adb_install.h | 4 | ||||
-rw-r--r-- | fuse_sdcard_provider.cpp | 46 | ||||
-rw-r--r-- | fuse_sideload.cpp | 39 | ||||
-rw-r--r-- | fuse_sideload.h | 2 | ||||
-rw-r--r-- | install.cpp | 21 | ||||
-rw-r--r-- | recovery.cpp | 38 | ||||
-rw-r--r-- | ui.cpp | 9 | ||||
-rw-r--r-- | ui.h | 3 |
9 files changed, 195 insertions, 93 deletions
diff --git a/adb_install.cpp b/adb_install.cpp index 4cfcb2a..a67973d 100644 --- a/adb_install.cpp +++ b/adb_install.cpp @@ -34,6 +34,7 @@ #include "fuse_sideload.h" static RecoveryUI* ui = NULL; +static pthread_t sideload_thread; static void set_usb_driver(bool enabled) { @@ -66,69 +67,80 @@ maybe_restart_adbd() { } } +struct sideload_data { + bool* wipe_cache; + const char* install_file; + bool cancel; + int result; +}; + +static struct sideload_data sideload_data; + // How long (in seconds) we wait for the host to start sending us a // package, before timing out. #define ADB_INSTALL_TIMEOUT 300 -int -apply_from_adb(RecoveryUI* ui_, bool* wipe_cache, const char* install_file) { - modified_flash = true; - - ui = ui_; - - stop_adbd(); - set_usb_driver(true); - - ui->Print("\n\nNow send the package you want to apply\n" - "to the device with \"adb sideload <filename>\"...\n"); - +void *adb_sideload_thread(void* v) { pid_t child; if ((child = fork()) == 0) { execl("/sbin/recovery", "recovery", "--adbd", NULL); _exit(-1); } + time_t start_time = time(NULL); + time_t now = start_time; + // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host // connects and starts serving a package. Poll for its // appearance. (Note that inotify doesn't work with FUSE.) - int result = INSTALL_ERROR; - int status; - bool waited = false; + int result = INSTALL_NONE; + int status = -1; struct stat st; - for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) { - if (waitpid(child, &status, WNOHANG) != 0) { + while (now - start_time < ADB_INSTALL_TIMEOUT) { + /* + * Exit if either: + * - The adb child process dies, or + * - The ui tells us to cancel + */ + if (kill(child, 0) != 0) { result = INSTALL_ERROR; - waited = true; break; } - if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) { - if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT-1) { - sleep(1); - continue; - } else { - ui->Print("\nTimed out waiting for package.\n\n"); - result = INSTALL_ERROR; - kill(child, SIGKILL); - break; - } + if (sideload_data.cancel) { + break; + } + + status = stat(FUSE_SIDELOAD_HOST_PATHNAME, &st); + if (status == 0) { + break; } - result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false); - break; + if (errno != ENOENT && errno != ENOTCONN) { + ui->Print("\nError %s waiting for package\n\n", strerror(errno)); + result = INSTALL_ERROR; + break; + } + + sleep(1); + now = time(NULL); } - if (!waited) { - // Calling stat() on this magic filename signals the minadbd - // subprocess to shut down. - stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); + if (status == 0) { + // Signal UI thread that we can no longer cancel + ui->CancelWaitKey(); + + result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, + sideload_data.wipe_cache, + sideload_data.install_file, + false); - // TODO(dougz): there should be a way to cancel waiting for a - // package (by pushing some button combo on the device). For now - // you just have to 'adb sideload' a file that's not a valid - // package, like "/dev/null". - waitpid(child, &status, 0); + sideload_data.result = result; } + // Ensure adb exits + kill(child, SIGTERM); + waitpid(child, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { if (WEXITSTATUS(status) == 3) { ui->Print("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n"); @@ -137,8 +149,40 @@ apply_from_adb(RecoveryUI* ui_, bool* wipe_cache, const char* install_file) { } } - set_usb_driver(false); + LOGI("sideload thread finished\n"); + return NULL; +} + +void +start_sideload(RecoveryUI* ui_, bool* wipe_cache, const char* install_file) { + modified_flash = true; + + ui = ui_; + + stop_adbd(); + set_usb_driver(true); + + ui->Print("\n\nNow send the package you want to apply\n" + "to the device with \"adb sideload <filename>\"...\n"); + + sideload_data.wipe_cache = wipe_cache; + sideload_data.install_file = install_file; + sideload_data.cancel = false; + sideload_data.result = INSTALL_NONE; + + pthread_create(&sideload_thread, NULL, &adb_sideload_thread, NULL); +} + +void stop_sideload() { + sideload_data.cancel = true; +} + +int wait_sideload() { + pthread_join(sideload_thread, NULL); + + ui->FlushKeys(); + maybe_restart_adbd(); - return result; + return sideload_data.result; } diff --git a/adb_install.h b/adb_install.h index efad436..7c9d7bc 100644 --- a/adb_install.h +++ b/adb_install.h @@ -19,6 +19,8 @@ class RecoveryUI; -int apply_from_adb(RecoveryUI* h, bool* wipe_cache, const char* install_file); +void start_sideload(RecoveryUI* h, bool* wipe_cache, const char* install_file); +void stop_sideload(); +int wait_sideload(); #endif diff --git a/fuse_sdcard_provider.cpp b/fuse_sdcard_provider.cpp index 879f90b..8681425 100644 --- a/fuse_sdcard_provider.cpp +++ b/fuse_sdcard_provider.cpp @@ -21,6 +21,7 @@ #include <pthread.h> #include <sys/mount.h> #include <sys/stat.h> +#include <sys/wait.h> #include <unistd.h> #include <fcntl.h> @@ -61,7 +62,7 @@ static void close_file(void* cookie) { } struct token { - pthread_t th; + pid_t pid; const char* path; int result; }; @@ -103,19 +104,30 @@ void* start_sdcard_fuse(const char* path) { token* t = new token; t->path = path; - pthread_create(&(t->th), NULL, run_sdcard_fuse, t); - - struct stat st; - int i; - for (i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) { - if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) { - if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT-1) { - sleep(1); - continue; - } else { - return NULL; - } + if ((t->pid = fork()) < 0) { + free(t); + return NULL; + } + if (t->pid == 0) { + run_sdcard_fuse(t); + _exit(0); + } + + time_t start_time = time(NULL); + time_t now = start_time; + + while (now - start_time < SDCARD_INSTALL_TIMEOUT) { + struct stat st; + if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) == 0) { + break; + } + if (errno != ENOENT && errno != ENOTCONN) { + free(t); + t = NULL; + break; } + sleep(1); + now = time(NULL); } return t; @@ -125,11 +137,9 @@ void finish_sdcard_fuse(void* cookie) { if (cookie == NULL) return; token* t = reinterpret_cast<token*>(cookie); - // Calling stat() on this magic filename signals the fuse - // filesystem to shut down. - struct stat st; - stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); + kill(t->pid, SIGTERM); + int status; + waitpid(t->pid, &status, 0); - pthread_join(t->th, NULL); delete t; } diff --git a/fuse_sideload.cpp b/fuse_sideload.cpp index 9c3e75f..39c7237 100644 --- a/fuse_sideload.cpp +++ b/fuse_sideload.cpp @@ -65,10 +65,8 @@ #include "fuse_sideload.h" #define PACKAGE_FILE_ID (FUSE_ROOT_ID+1) -#define EXIT_FLAG_ID (FUSE_ROOT_ID+2) #define NO_STATUS 1 -#define NO_STATUS_EXIT 2 struct fuse_data { int ffd; // file descriptor for the fuse socket @@ -179,14 +177,12 @@ static int handle_getattr(void* /* data */, struct fuse_data* fd, const struct f fill_attr(&(out.attr), fd, hdr->nodeid, 4096, S_IFDIR | 0555); } else if (hdr->nodeid == PACKAGE_FILE_ID) { fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); - } else if (hdr->nodeid == EXIT_FLAG_ID) { - fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); } else { return -ENOENT; } fuse_reply(fd, hdr->unique, &out, sizeof(out)); - return (hdr->nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; + return NO_STATUS; } static int handle_lookup(void* data, struct fuse_data* fd, @@ -201,21 +197,15 @@ static int handle_lookup(void* data, struct fuse_data* fd, out.nodeid = PACKAGE_FILE_ID; out.generation = PACKAGE_FILE_ID; fill_attr(&(out.attr), fd, PACKAGE_FILE_ID, fd->file_size, S_IFREG | 0444); - } else if (strncmp(FUSE_SIDELOAD_HOST_EXIT_FLAG, reinterpret_cast<const char*>(data), - sizeof(FUSE_SIDELOAD_HOST_EXIT_FLAG)) == 0) { - out.nodeid = EXIT_FLAG_ID; - out.generation = EXIT_FLAG_ID; - fill_attr(&(out.attr), fd, EXIT_FLAG_ID, 0, S_IFREG | 0); } else { return -ENOENT; } fuse_reply(fd, hdr->unique, &out, sizeof(out)); - return (out.nodeid == EXIT_FLAG_ID) ? NO_STATUS_EXIT : NO_STATUS; + return NO_STATUS; } static int handle_open(void* /* data */, struct fuse_data* fd, const struct fuse_in_header* hdr) { - if (hdr->nodeid == EXIT_FLAG_ID) return -EPERM; if (hdr->nodeid != PACKAGE_FILE_ID) return -ENOENT; struct fuse_open_out out; @@ -361,6 +351,12 @@ static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_he return NO_STATUS; } +static volatile int terminated = 0; +static void sig_term(int sig) +{ + terminated = 1; +} + int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_size, uint32_t block_size) { @@ -418,6 +414,8 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, goto done; } + signal(SIGTERM, sig_term); + fd.ffd = open("/dev/fuse", O_RDWR); if (fd.ffd < 0) { perror("open /dev/fuse"); @@ -438,7 +436,17 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, goto done; } uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX*8]; - for (;;) { + while (!terminated) { + fd_set fds; + struct timeval tv; + FD_ZERO(&fds); + FD_SET(fd.ffd, &fds); + tv.tv_sec = 1; + tv.tv_usec = 0; + int rc = select(fd.ffd+1, &fds, NULL, NULL, &tv); + if (rc <= 0) { + continue; + } ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer))); if (len == -1) { perror("read request"); @@ -493,11 +501,6 @@ int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, break; } - if (result == NO_STATUS_EXIT) { - result = 0; - break; - } - if (result != NO_STATUS) { struct fuse_out_header outhdr; outhdr.len = sizeof(outhdr); diff --git a/fuse_sideload.h b/fuse_sideload.h index c0b16ef..71a09a6 100644 --- a/fuse_sideload.h +++ b/fuse_sideload.h @@ -21,8 +21,6 @@ #define FUSE_SIDELOAD_HOST_MOUNTPOINT "/sideload" #define FUSE_SIDELOAD_HOST_FILENAME "package.zip" #define FUSE_SIDELOAD_HOST_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_FILENAME) -#define FUSE_SIDELOAD_HOST_EXIT_FLAG "exit" -#define FUSE_SIDELOAD_HOST_EXIT_PATHNAME (FUSE_SIDELOAD_HOST_MOUNTPOINT "/" FUSE_SIDELOAD_HOST_EXIT_FLAG) struct provider_vtab { // read a block diff --git a/install.cpp b/install.cpp index 1bdf46b..7afcff6 100644 --- a/install.cpp +++ b/install.cpp @@ -22,6 +22,7 @@ #include <sys/stat.h> #include <sys/wait.h> #include <unistd.h> +#include <setjmp.h> #include <sys/mount.h> #include "common.h" @@ -47,6 +48,12 @@ static const float VERIFICATION_PROGRESS_FRACTION = 0.25; static const float DEFAULT_FILES_PROGRESS_FRACTION = 0.4; static const float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1; +static jmp_buf jb; +static void sig_bus(int sig) +{ + longjmp(jb, 1); +} + // If the package contains an update binary, extract it and run it. static int try_update_binary(const char* path, ZipArchive* zip, bool* wipe_cache) { @@ -306,7 +313,19 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount) ui->Print("Verifying update package...\n"); int err; - err = verify_file(map.addr, map.length, loadedKeys, numKeys); + + // Because we mmap() the update file which is backed by FUSE, we get + // SIGBUS when the host aborts the transfer. We handle this by using + // setjmp/longjmp. + signal(SIGBUS, sig_bus); + if (setjmp(jb) == 0) { + err = verify_file(map.addr, map.length, loadedKeys, numKeys); + } + else { + err = VERIFY_FAILURE; + } + signal(SIGBUS, SIG_DFL); + free(loadedKeys); LOGI("verify_file returned %d\n", err); if (err != VERIFY_SUCCESS) { diff --git a/recovery.cpp b/recovery.cpp index 3cd2764..5c482e0 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -616,8 +616,8 @@ get_menu_selection(const char* const * headers, const char* const * items, ui->EndMenu(); return 0; // XXX fixme } - } else if (key == -2) { // we are returning from ui_cancel_wait_key(): trigger a GO_BACK - return Device::kGoBack; + } else if (key == -2) { // we are returning from ui_cancel_wait_key(): no action + return Device::kNoAction; } else if (key == -6) { return Device::kRefresh; @@ -918,7 +918,18 @@ refresh: break; } if (chosen == item_sideload) { - status = apply_from_adb(ui, &wipe_cache, TEMPORARY_INSTALL_FILE); + static const char* headers[] = { "ADB Sideload", + "", + NULL + }; + static const char* list[] = { "Cancel sideload", NULL }; + + start_sideload(ui, &wipe_cache, TEMPORARY_INSTALL_FILE); + int item = get_menu_selection(headers, list, 0, 0, device); + if (item != Device::kNoAction) { + stop_sideload(); + } + status = wait_sideload(); } else { std::string id = volumes[chosen - 1].mId; @@ -991,14 +1002,16 @@ prompt_and_wait(Device* device, int status) { } } - if (status != INSTALL_SUCCESS) { - ui->SetBackground(RecoveryUI::ERROR); - ui->Print("Installation aborted.\n"); - copy_logs(); - } else if (!ui->IsTextVisible()) { - return Device::NO_ACTION; // reboot if logs aren't visible - } else { - ui->Print("\nInstall complete.\n"); + if (status >= 0 && status != INSTALL_NONE) { + if (status != INSTALL_SUCCESS) { + ui->SetBackground(RecoveryUI::ERROR); + ui->Print("Installation aborted.\n"); + copy_logs(); + } else if (!ui->IsTextVisible()) { + return Device::NO_ACTION; // reboot if logs aren't visible + } else { + ui->Print("\nInstall complete.\n"); + } } break; } @@ -1338,7 +1351,8 @@ main(int argc, char **argv) { if (!sideload_auto_reboot) { ui->ShowText(true); } - status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE); + start_sideload(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE); + status = wait_sideload(); if (status == INSTALL_SUCCESS && should_wipe_cache) { if (!wipe_cache(false, device)) { status = INSTALL_ERROR; @@ -255,6 +255,15 @@ int RecoveryUI::WaitKey() { return key; } +void RecoveryUI::CancelWaitKey() +{ + pthread_mutex_lock(&key_queue_mutex); + key_queue[key_queue_len] = -2; + key_queue_len++; + pthread_cond_signal(&key_queue_cond); + pthread_mutex_unlock(&key_queue_mutex); +} + bool RecoveryUI::IsUsbConnected() { int fd = open("/sys/class/android_usb/android0/state", O_RDONLY); if (fd < 0) { @@ -76,6 +76,9 @@ class RecoveryUI { // Wait for a key and return it. May return -1 after timeout. virtual int WaitKey(); + // Cancel a WaitKey() + virtual void CancelWaitKey(); + virtual bool IsKeyPressed(int key); virtual bool IsLongPress(); |