Separate uncrypt into two modes

uncrypt needs to be triggered to prepare the OTA package before
rebooting into the recovery. Separate uncrypt into two modes. In
mode 1, it uncrypts the OTA package, but will not reboot the
device. In mode 2, it wipes the /misc partition and reboots.

Needs matching changes in frameworks/base, system/core and
external/sepolicy to work properly.

Bug: 20012567
Bug: 20949086
Change-Id: I14d25cb62770dd405cb56824d05d649c3a94f315
This commit is contained in:
Tao Bao 2015-05-21 16:44:44 -07:00
parent 1857a7f579
commit 158e11d673
2 changed files with 96 additions and 99 deletions

View file

@ -20,6 +20,6 @@ LOCAL_SRC_FILES := uncrypt.cpp
LOCAL_MODULE := uncrypt LOCAL_MODULE := uncrypt
LOCAL_STATIC_LIBRARIES := libfs_mgr liblog libcutils LOCAL_STATIC_LIBRARIES := libbase liblog libfs_mgr libcutils
include $(BUILD_EXECUTABLE) include $(BUILD_EXECUTABLE)

View file

@ -40,25 +40,29 @@
// file data to use as an update package. // file data to use as an update package.
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define LOG_TAG "uncrypt" #include <base/file.h>
#include <log/log.h> #include <base/strings.h>
#include <cutils/properties.h> #include <cutils/properties.h>
#include <fs_mgr.h> #include <fs_mgr.h>
#define LOG_TAG "uncrypt"
#include <log/log.h>
#define WINDOW_SIZE 5 #define WINDOW_SIZE 5
#define RECOVERY_COMMAND_FILE "/cache/recovery/command"
#define RECOVERY_COMMAND_FILE_TMP "/cache/recovery/command.tmp" static const std::string cache_block_map = "/cache/recovery/block.map";
#define CACHE_BLOCK_MAP "/cache/recovery/block.map" static const std::string status_file = "/cache/recovery/uncrypt_status";
static const std::string uncrypt_file = "/cache/recovery/uncrypt_file";
static struct fstab* fstab = NULL; static struct fstab* fstab = NULL;
@ -155,65 +159,35 @@ static const char* find_block_device(const char* path, bool* encryptable, bool*
return NULL; return NULL;
} }
// Parse the command file RECOVERY_COMMAND_FILE to find the update package // Parse uncrypt_file to find the update package name.
// name. If it's on the /data partition, replace the package name with the static bool find_uncrypt_package(std::string& package_name)
// block map file name and store it temporarily in RECOVERY_COMMAND_FILE_TMP.
// It will be renamed to RECOVERY_COMMAND_FILE if uncrypt finishes
// successfully.
static char* find_update_package()
{ {
FILE* f = fopen(RECOVERY_COMMAND_FILE, "r"); if (!android::base::ReadFileToString(uncrypt_file, &package_name)) {
if (f == NULL) { ALOGE("failed to open \"%s\": %s\n", uncrypt_file.c_str(), strerror(errno));
return NULL; return false;
} }
int fd = open(RECOVERY_COMMAND_FILE_TMP, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
if (fd < 0) {
ALOGE("failed to open %s\n", RECOVERY_COMMAND_FILE_TMP);
return NULL;
}
FILE* fo = fdopen(fd, "w");
char* fn = NULL;
char* line = NULL;
size_t len = 0;
while (getline(&line, &len, f) != -1) {
if (strncmp(line, "--update_package=", strlen("--update_package=")) == 0) {
fn = strdup(line + strlen("--update_package="));
// Replace the package name with block map file if it's on /data partition.
if (strncmp(fn, "/data/", strlen("/data/")) == 0) {
fputs("--update_package=@" CACHE_BLOCK_MAP "\n", fo);
continue;
}
}
fputs(line, fo);
}
free(line);
fclose(f);
if (fsync(fd) == -1) {
ALOGE("failed to fsync \"%s\": %s\n", RECOVERY_COMMAND_FILE_TMP, strerror(errno));
fclose(fo);
return NULL;
}
fclose(fo);
if (fn) { // Remove the trailing '\n' if present.
char* newline = strchr(fn, '\n'); package_name = android::base::Trim(package_name);
if (newline) {
*newline = 0; return true;
}
}
return fn;
} }
static int produce_block_map(const char* path, const char* map_file, const char* blk_dev, static int produce_block_map(const char* path, const char* map_file, const char* blk_dev,
bool encrypted) { bool encrypted, int status_fd) {
int mapfd = open(map_file, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR); int mapfd = open(map_file, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
if (mapfd < 0) { if (mapfd == -1) {
ALOGE("failed to open %s\n", map_file); ALOGE("failed to open %s\n", map_file);
return -1; return -1;
} }
FILE* mapf = fdopen(mapfd, "w"); FILE* mapf = fdopen(mapfd, "w");
// Make sure we can write to the status_file.
if (!android::base::WriteStringToFd("0\n", status_fd)) {
ALOGE("failed to update \"%s\"\n", status_file.c_str());
return -1;
}
struct stat sb; struct stat sb;
int ret = stat(path, &sb); int ret = stat(path, &sb);
if (ret != 0) { if (ret != 0) {
@ -259,7 +233,15 @@ static int produce_block_map(const char* path, const char* map_file, const char*
} }
} }
int last_progress = 0;
while (pos < sb.st_size) { while (pos < sb.st_size) {
// Update the status file, progress must be between [0, 99].
int progress = static_cast<int>(100 * (double(pos) / double(sb.st_size)));
if (progress > last_progress) {
last_progress = progress;
android::base::WriteStringToFd(std::to_string(progress) + "\n", status_fd);
}
if ((tail+1) % WINDOW_SIZE == head) { if ((tail+1) % WINDOW_SIZE == head) {
// write out head buffer // write out head buffer
int block = head_block; int block = head_block;
@ -380,43 +362,15 @@ static void reboot_to_recovery() {
ALOGE("reboot didn't succeed?"); ALOGE("reboot didn't succeed?");
} }
int main(int argc, char** argv) int uncrypt(const char* input_path, const char* map_file, int status_fd) {
{
const char* input_path;
const char* map_file;
bool do_reboot = true;
if (argc != 1 && argc != 3) { ALOGI("update package is \"%s\"", input_path);
fprintf(stderr, "usage: %s [<transform_path> <map_file>]\n", argv[0]);
return 2;
}
if (argc == 3) {
// when command-line args are given this binary is being used
// for debugging; don't reboot to recovery at the end.
input_path = argv[1];
map_file = argv[2];
do_reboot = false;
} else {
input_path = find_update_package();
if (input_path == NULL) {
// if we're rebooting to recovery without a package (say,
// to wipe data), then we don't need to do anything before
// going to recovery.
ALOGI("no recovery command file or no update package arg");
reboot_to_recovery();
return 1;
}
map_file = CACHE_BLOCK_MAP;
}
ALOGI("update package is %s", input_path);
// Turn the name of the file we're supposed to convert into an // Turn the name of the file we're supposed to convert into an
// absolute path, so we can find what filesystem it's on. // absolute path, so we can find what filesystem it's on.
char path[PATH_MAX+1]; char path[PATH_MAX+1];
if (realpath(input_path, path) == NULL) { if (realpath(input_path, path) == NULL) {
ALOGE("failed to convert %s to absolute path: %s", input_path, strerror(errno)); ALOGE("failed to convert \"%s\" to absolute path: %s", input_path, strerror(errno));
return 1; return 1;
} }
@ -445,21 +399,64 @@ int main(int argc, char** argv)
// On /data we want to convert the file to a block map so that we // On /data we want to convert the file to a block map so that we
// can read the package without mounting the partition. On /cache // can read the package without mounting the partition. On /cache
// and /sdcard we leave the file alone. // and /sdcard we leave the file alone.
if (strncmp(path, "/data/", 6) != 0) { if (strncmp(path, "/data/", 6) == 0) {
// path does not start with "/data/"; leave it alone.
unlink(RECOVERY_COMMAND_FILE_TMP);
wipe_misc();
} else {
ALOGI("writing block map %s", map_file); ALOGI("writing block map %s", map_file);
if (produce_block_map(path, map_file, blk_dev, encrypted) != 0) { if (produce_block_map(path, map_file, blk_dev, encrypted, status_fd) != 0) {
return 1;
}
}
return 0;
}
int main(int argc, char** argv) {
const char* input_path;
const char* map_file;
if (argc != 3 && argc != 1 && (argc == 2 && strcmp(argv[1], "--reboot") != 0)) {
fprintf(stderr, "usage: %s [--reboot] [<transform_path> <map_file>]\n", argv[0]);
return 2;
}
// When uncrypt is started with "--reboot", it wipes misc and reboots.
// Otherwise it uncrypts the package and writes the block map.
if (argc == 2) {
if (read_fstab() == NULL) {
return 1; return 1;
} }
wipe_misc(); wipe_misc();
rename(RECOVERY_COMMAND_FILE_TMP, RECOVERY_COMMAND_FILE); reboot_to_recovery();
} else {
std::string package;
if (argc == 3) {
// when command-line args are given this binary is being used
// for debugging.
input_path = argv[1];
map_file = argv[2];
} else {
if (!find_uncrypt_package(package)) {
return 1;
}
input_path = package.c_str();
map_file = cache_block_map.c_str();
} }
if (do_reboot) { // The pipe has been created by the system server.
reboot_to_recovery(); int status_fd = open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
if (status_fd == -1) {
ALOGE("failed to open pipe \"%s\": %s\n", status_file.c_str(), strerror(errno));
return 1;
} }
int status = uncrypt(input_path, map_file, status_fd);
if (status != 0) {
android::base::WriteStringToFd("-1\n", status_fd);
close(status_fd);
return 1;
}
android::base::WriteStringToFd("100\n", status_fd);
close(status_fd);
}
return 0; return 0;
} }