recovery: Fork a process for fuse when sideloading from SD card.

For applying update from SD card, we used to use a thread to serve the
file with fuse. Since accessing through fuse involves going from kernel
to userspace to kernel, it may run into deadlock (e.g. for mmap_sem)
when a page fault occurs. Switch to using a process instead.

Bug: 23783099
Bug: 26313124
Change-Id: Iac0f55b1bdb078cadb520cfe1133e70fbb26eadd
This commit is contained in:
Tao Bao 2016-01-13 15:05:20 -08:00
parent ab9db5284e
commit cdcf28f54f
3 changed files with 69 additions and 69 deletions

View file

@ -18,7 +18,6 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <pthread.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
@ -60,81 +59,30 @@ static void close_file(void* cookie) {
close(fd->fd); close(fd->fd);
} }
struct token { bool start_sdcard_fuse(const char* path) {
pthread_t th;
const char* path;
int result;
};
static void* run_sdcard_fuse(void* cookie) {
token* t = reinterpret_cast<token*>(cookie);
struct stat sb; struct stat sb;
if (stat(t->path, &sb) < 0) { if (stat(path, &sb) == -1) {
fprintf(stderr, "failed to stat %s: %s\n", t->path, strerror(errno)); fprintf(stderr, "failed to stat %s: %s\n", path, strerror(errno));
t->result = -1; return false;
return NULL;
} }
struct file_data fd; file_data fd;
struct provider_vtab vtab; fd.fd = open(path, O_RDONLY);
if (fd.fd == -1) {
fd.fd = open(t->path, O_RDONLY); fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno));
if (fd.fd < 0) { return false;
fprintf(stderr, "failed to open %s: %s\n", t->path, strerror(errno));
t->result = -1;
return NULL;
} }
fd.file_size = sb.st_size; fd.file_size = sb.st_size;
fd.block_size = 65536; fd.block_size = 65536;
provider_vtab vtab;
vtab.read_block = read_block_file; vtab.read_block = read_block_file;
vtab.close = close_file; vtab.close = close_file;
t->result = run_fuse_sideload(&vtab, &fd, fd.file_size, fd.block_size);
return NULL;
}
// How long (in seconds) we wait for the fuse-provided package file to
// appear, before timing out.
#define SDCARD_INSTALL_TIMEOUT 10
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;
}
}
}
// The installation process expects to find the sdcard unmounted. // The installation process expects to find the sdcard unmounted.
// Unmount it with MNT_DETACH so that our open file continues to // Unmount it with MNT_DETACH so that our open file continues to
// work but new references see it as unmounted. // work but new references see it as unmounted.
umount2("/sdcard", MNT_DETACH); umount2("/sdcard", MNT_DETACH);
return t; return run_fuse_sideload(&vtab, &fd, fd.file_size, fd.block_size) == 0;
}
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);
pthread_join(t->th, NULL);
delete t;
} }

View file

@ -17,7 +17,6 @@
#ifndef __FUSE_SDCARD_PROVIDER_H #ifndef __FUSE_SDCARD_PROVIDER_H
#define __FUSE_SDCARD_PROVIDER_H #define __FUSE_SDCARD_PROVIDER_H
void* start_sdcard_fuse(const char* path); bool start_sdcard_fuse(const char* path);
void finish_sdcard_fuse(void* token);
#endif #endif

View file

@ -28,6 +28,7 @@
#include <sys/klog.h> #include <sys/klog.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
@ -833,6 +834,10 @@ static void choose_recovery_file(Device* device) {
} }
} }
// How long (in seconds) we wait for the fuse-provided package file to
// appear, before timing out.
#define SDCARD_INSTALL_TIMEOUT 10
static int apply_from_sdcard(Device* device, bool* wipe_cache) { static int apply_from_sdcard(Device* device, bool* wipe_cache) {
modified_flash = true; modified_flash = true;
@ -850,14 +855,62 @@ static int apply_from_sdcard(Device* device, bool* wipe_cache) {
ui->Print("\n-- Install %s ...\n", path); ui->Print("\n-- Install %s ...\n", path);
set_sdcard_update_bootloader_message(); set_sdcard_update_bootloader_message();
void* token = start_sdcard_fuse(path);
int status = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, // We used to use fuse in a thread as opposed to a process. Since accessing
// through fuse involves going from kernel to userspace to kernel, it leads
// to deadlock when a page fault occurs. (Bug: 26313124)
pid_t child;
if ((child = fork()) == 0) {
bool status = start_sdcard_fuse(path);
_exit(status ? EXIT_SUCCESS : EXIT_FAILURE);
}
// FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the fuse in child
// process is ready.
int result = INSTALL_ERROR;
int status;
bool waited = false;
for (int i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) {
if (waitpid(child, &status, WNOHANG) == -1) {
result = INSTALL_ERROR;
waited = true;
break;
}
struct stat sb;
if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &sb) == -1) {
if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT-1) {
sleep(1);
continue;
} else {
LOGE("Timed out waiting for the fuse-provided package.\n");
result = INSTALL_ERROR;
kill(child, SIGKILL);
break;
}
}
result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache,
TEMPORARY_INSTALL_FILE, false); TEMPORARY_INSTALL_FILE, false);
break;
}
if (!waited) {
// Calling stat() on this magic filename signals the fuse
// filesystem to shut down.
struct stat sb;
stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &sb);
waitpid(child, &status, 0);
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
LOGE("Error exit from the fuse process: %d\n", WEXITSTATUS(status));
}
finish_sdcard_fuse(token);
ensure_path_unmounted(SDCARD_ROOT); ensure_path_unmounted(SDCARD_ROOT);
return status; return result;
} }
// Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION // Return REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION