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:
parent
ab9db5284e
commit
cdcf28f54f
3 changed files with 69 additions and 69 deletions
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
61
recovery.cpp
61
recovery.cpp
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue