diff options
-rw-r--r-- | Android.mk | 3 | ||||
-rw-r--r-- | fuse_sdcard_provider.c | 141 | ||||
-rw-r--r-- | fuse_sdcard_provider.h | 23 | ||||
-rw-r--r-- | recovery.cpp | 110 |
4 files changed, 175 insertions, 102 deletions
diff --git a/Android.mk b/Android.mk index f469182ef..1a91f0029 100644 --- a/Android.mk +++ b/Android.mk @@ -38,7 +38,8 @@ LOCAL_SRC_FILES := \ screen_ui.cpp \ asn1_decoder.cpp \ verifier.cpp \ - adb_install.cpp + adb_install.cpp \ + fuse_sdcard_provider.c LOCAL_MODULE := recovery diff --git a/fuse_sdcard_provider.c b/fuse_sdcard_provider.c new file mode 100644 index 000000000..19fb52df0 --- /dev/null +++ b/fuse_sdcard_provider.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2014 The Android Open Source 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 <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <pthread.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + +#include "fuse_sideload.h" + +struct file_data { + int fd; // the underlying sdcard file + + uint64_t file_size; + uint32_t block_size; +}; + +static int read_block_file(void* cookie, uint32_t block, uint8_t* buffer, uint32_t fetch_size) { + struct file_data* fd = (struct file_data*)cookie; + + if (lseek(fd->fd, block * fd->block_size, SEEK_SET) < 0) { + printf("seek on sdcard failed: %s\n", strerror(errno)); + return -EIO; + } + + while (fetch_size > 0) { + ssize_t r = read(fd->fd, buffer, fetch_size); + if (r < 0) { + if (r != -EINTR) { + printf("read on sdcard failed: %s\n", strerror(errno)); + return -EIO; + } + r = 0; + } + fetch_size -= r; + buffer += r; + } + + return 0; +} + +static void close_file(void* cookie) { + struct file_data* fd = (struct file_data*)cookie; + close(fd->fd); +} + +struct token { + pthread_t th; + const char* path; + int result; +}; + +static void* run_sdcard_fuse(void* cookie) { + struct token* t = (struct token*)cookie; + + struct stat sb; + if (stat(t->path, &sb) < 0) { + fprintf(stderr, "failed to stat %s: %s\n", t->path, strerror(errno)); + t->result = -1; + return NULL; + } + + struct file_data fd; + struct provider_vtab vtab; + + fd.fd = open(t->path, O_RDONLY); + if (fd.fd < 0) { + fprintf(stderr, "failed to open %s: %s\n", t->path, strerror(errno)); + t->result = -1; + return NULL; + } + fd.file_size = sb.st_size; + fd.block_size = 65536; + + vtab.read_block = read_block_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) { + struct token* t = malloc(sizeof(struct 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. + // Unmount it with MNT_DETACH so that our open file continues to + // work but new references see it as unmounted. + umount2("/sdcard", MNT_DETACH); + + return t; +} + +void finish_sdcard_fuse(void* cookie) { + if (cookie == NULL) return; + struct token* t = (struct 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); + free(t); +} diff --git a/fuse_sdcard_provider.h b/fuse_sdcard_provider.h new file mode 100644 index 000000000..dc2982ca0 --- /dev/null +++ b/fuse_sdcard_provider.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2014 The Android Open Source 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. + */ + +#ifndef __FUSE_SDCARD_PROVIDER_H +#define __FUSE_SDCARD_PROVIDER_H + +void* start_sdcard_fuse(const char* path); +void finish_sdcard_fuse(void* token); + +#endif diff --git a/recovery.cpp b/recovery.cpp index 1c7c0d6a4..d2d85e7c7 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -44,6 +44,8 @@ #include "adb_install.h" extern "C" { #include "minadbd/adb.h" +#include "fuse_sideload.h" +#include "fuse_sdcard_provider.h" } struct selabel_handle *sehandle; @@ -73,7 +75,6 @@ static const char *CACHE_ROOT = "/cache"; static const char *SDCARD_ROOT = "/sdcard"; static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; -static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload"; RecoveryUI* ui = NULL; char* locale = NULL; @@ -439,96 +440,6 @@ erase_volume(const char *volume) { return result; } -static char* -copy_sideloaded_package(const char* original_path) { - if (ensure_path_mounted(original_path) != 0) { - LOGE("Can't mount %s\n", original_path); - return NULL; - } - - if (ensure_path_mounted(SIDELOAD_TEMP_DIR) != 0) { - LOGE("Can't mount %s\n", SIDELOAD_TEMP_DIR); - return NULL; - } - - if (mkdir(SIDELOAD_TEMP_DIR, 0700) != 0) { - if (errno != EEXIST) { - LOGE("Can't mkdir %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno)); - return NULL; - } - } - - // verify that SIDELOAD_TEMP_DIR is exactly what we expect: a - // directory, owned by root, readable and writable only by root. - struct stat st; - if (stat(SIDELOAD_TEMP_DIR, &st) != 0) { - LOGE("failed to stat %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno)); - return NULL; - } - if (!S_ISDIR(st.st_mode)) { - LOGE("%s isn't a directory\n", SIDELOAD_TEMP_DIR); - return NULL; - } - if ((st.st_mode & 0777) != 0700) { - LOGE("%s has perms %o\n", SIDELOAD_TEMP_DIR, st.st_mode); - return NULL; - } - if (st.st_uid != 0) { - LOGE("%s owned by %lu; not root\n", SIDELOAD_TEMP_DIR, st.st_uid); - return NULL; - } - - char copy_path[PATH_MAX]; - strcpy(copy_path, SIDELOAD_TEMP_DIR); - strcat(copy_path, "/package.zip"); - - char* buffer = (char*)malloc(BUFSIZ); - if (buffer == NULL) { - LOGE("Failed to allocate buffer\n"); - return NULL; - } - - size_t read; - FILE* fin = fopen(original_path, "rb"); - if (fin == NULL) { - LOGE("Failed to open %s (%s)\n", original_path, strerror(errno)); - return NULL; - } - FILE* fout = fopen(copy_path, "wb"); - if (fout == NULL) { - LOGE("Failed to open %s (%s)\n", copy_path, strerror(errno)); - return NULL; - } - - while ((read = fread(buffer, 1, BUFSIZ, fin)) > 0) { - if (fwrite(buffer, 1, read, fout) != read) { - LOGE("Short write of %s (%s)\n", copy_path, strerror(errno)); - return NULL; - } - } - - free(buffer); - - if (fclose(fout) != 0) { - LOGE("Failed to close %s (%s)\n", copy_path, strerror(errno)); - return NULL; - } - - if (fclose(fin) != 0) { - LOGE("Failed to close %s (%s)\n", original_path, strerror(errno)); - return NULL; - } - - // "adb push" is happy to overwrite read-only files when it's - // running as root, but we'll try anyway. - if (chmod(copy_path, 0400) != 0) { - LOGE("Failed to chmod %s (%s)\n", copy_path, strerror(errno)); - return NULL; - } - - return strdup(copy_path); -} - static const char** prepend_title(const char* const* headers) { // count the number of lines in our title, plus the @@ -812,17 +723,13 @@ prompt_and_wait(Device* device, int status) { ui->Print("\n-- Install %s ...\n", path); set_sdcard_update_bootloader_message(); - char* copy = copy_sideloaded_package(path); - free(path); - ensure_path_unmounted(SDCARD_ROOT); + void* token = start_sdcard_fuse(path); - int status; - if (copy) { - status = install_package(copy, &wipe_cache, TEMPORARY_INSTALL_FILE, true); - free(copy); - } else { - status = INSTALL_ERROR; - } + int status = install_package(FUSE_SIDELOAD_HOST_PATHNAME, &wipe_cache, + TEMPORARY_INSTALL_FILE, false); + + finish_sdcard_fuse(token); + ensure_path_unmounted(SDCARD_ROOT); if (status == INSTALL_SUCCESS && wipe_cache) { ui->Print("\n-- Wiping cache (at package request)...\n"); @@ -832,6 +739,7 @@ prompt_and_wait(Device* device, int status) { ui->Print("Cache wipe complete.\n"); } } + if (status >= 0) { if (status != INSTALL_SUCCESS) { ui->SetBackground(RecoveryUI::ERROR); |