summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--etc/init.rc3
-rw-r--r--recovery.cpp39
-rw-r--r--uncrypt/uncrypt.cpp260
-rw-r--r--uncrypt/uncrypt.rc3
4 files changed, 216 insertions, 89 deletions
diff --git a/etc/init.rc b/etc/init.rc
index dc1865986..5915b8d80 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -1,6 +1,9 @@
import /init.recovery.${ro.hardware}.rc
on early-init
+ # Set the security context of /postinstall if present.
+ restorecon /postinstall
+
start ueventd
start healthd
diff --git a/recovery.cpp b/recovery.cpp
index 4ae685f6a..fe6793da0 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -101,6 +101,7 @@ char* locale = NULL;
char* stage = NULL;
char* reason = NULL;
bool modified_flash = false;
+static bool has_cache = false;
/*
* The recovery tool communicates with the main system through /cache files.
@@ -313,8 +314,8 @@ get_args(int *argc, char ***argv) {
}
}
- // --- if that doesn't work, try the command file
- if (*argc <= 1) {
+ // --- if that doesn't work, try the command file (if we have /cache).
+ if (*argc <= 1 && has_cache) {
FILE *fp = fopen_path(COMMAND_FILE, "r");
if (fp != NULL) {
char *token;
@@ -436,6 +437,11 @@ static void rotate_logs(int max) {
}
static void copy_logs() {
+ // We can do nothing for now if there's no /cache partition.
+ if (!has_cache) {
+ return;
+ }
+
// We only rotate and record the log of the current session if there are
// actual attempts to modify the flash, such as wipes, installs from BCB
// or menu selections. This is to avoid unnecessary rotation (and
@@ -467,7 +473,7 @@ static void copy_logs() {
static void
finish_recovery(const char *send_intent) {
// By this point, we're ready to return to the main system...
- if (send_intent != NULL) {
+ if (send_intent != NULL && has_cache) {
FILE *fp = fopen_path(INTENT_FILE, "w");
if (fp == NULL) {
LOGE("Can't open %s\n", INTENT_FILE);
@@ -480,7 +486,7 @@ finish_recovery(const char *send_intent) {
// Save the locale to cache, so if recovery is next started up
// without a --locale argument (eg, directly from the bootloader)
// it will use the last-known locale.
- if (locale != NULL) {
+ if (locale != NULL && has_cache) {
LOGI("Saving locale \"%s\"\n", locale);
FILE* fp = fopen_path(LOCALE_FILE, "w");
fwrite(locale, 1, strlen(locale), fp);
@@ -497,12 +503,13 @@ finish_recovery(const char *send_intent) {
set_bootloader_message(&boot);
// Remove the command file, so recovery won't repeat indefinitely.
- if (ensure_path_mounted(COMMAND_FILE) != 0 ||
- (unlink(COMMAND_FILE) && errno != ENOENT)) {
- LOGW("Can't unlink %s\n", COMMAND_FILE);
+ if (has_cache) {
+ if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) {
+ LOGW("Can't unlink %s\n", COMMAND_FILE);
+ }
+ ensure_path_unmounted(CACHE_ROOT);
}
- ensure_path_unmounted(CACHE_ROOT);
sync(); // For good measure.
}
@@ -793,7 +800,7 @@ static bool wipe_data(int should_confirm, Device* device) {
bool success =
device->PreWipeData() &&
erase_volume("/data") &&
- erase_volume("/cache") &&
+ (has_cache ? erase_volume("/cache") : true) &&
device->PostWipeData();
ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
return success;
@@ -801,6 +808,11 @@ static bool wipe_data(int should_confirm, Device* device) {
// Return true on success.
static bool wipe_cache(bool should_confirm, Device* device) {
+ if (!has_cache) {
+ ui->Print("No /cache partition found.\n");
+ return false;
+ }
+
if (should_confirm && !yes_no(device, "Wipe cache?", " THIS CAN NOT BE UNDONE!")) {
return false;
}
@@ -814,6 +826,11 @@ static bool wipe_cache(bool should_confirm, Device* device) {
}
static void choose_recovery_file(Device* device) {
+ if (!has_cache) {
+ ui->Print("No /cache partition found.\n");
+ return;
+ }
+
// "Back" + KEEP_LOG_COUNT * 2 + terminating nullptr entry
char* entries[1 + KEEP_LOG_COUNT * 2 + 1];
memset(entries, 0, sizeof(entries));
@@ -1167,6 +1184,8 @@ int main(int argc, char **argv) {
printf("Starting recovery (pid %d) on %s", getpid(), ctime(&start));
load_volume_table();
+ has_cache = volume_for_path(CACHE_ROOT) != nullptr;
+
get_args(&argc, &argv);
const char *send_intent = NULL;
@@ -1207,7 +1226,7 @@ int main(int argc, char **argv) {
}
}
- if (locale == NULL) {
+ if (locale == nullptr && has_cache) {
load_locale_from_cache();
}
printf("locale is [%s]\n", locale);
diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp
index 2a32108a3..e783b9e7a 100644
--- a/uncrypt/uncrypt.cpp
+++ b/uncrypt/uncrypt.cpp
@@ -39,6 +39,53 @@
// Recovery can take this block map file and retrieve the underlying
// file data to use as an update package.
+/**
+ * In addition to the uncrypt work, uncrypt also takes care of setting and
+ * clearing the bootloader control block (BCB) at /misc partition.
+ *
+ * uncrypt is triggered as init services on demand. It uses socket to
+ * communicate with its caller (i.e. system_server). The socket is managed by
+ * init (i.e. created prior to the service starts, and destroyed when uncrypt
+ * exits).
+ *
+ * Below is the uncrypt protocol.
+ *
+ * a. caller b. init c. uncrypt
+ * --------------- ------------ --------------
+ * a1. ctl.start:
+ * setup-bcb /
+ * clear-bcb /
+ * uncrypt
+ *
+ * b2. create socket at
+ * /dev/socket/uncrypt
+ *
+ * c3. listen and accept
+ *
+ * a4. send a 4-byte int
+ * (message length)
+ * c5. receive message length
+ * a6. send message
+ * c7. receive message
+ * c8. <do the work; may send
+ * the progress>
+ * a9. <may handle progress>
+ * c10. <upon finishing>
+ * send "100" or "-1"
+ *
+ * a11. receive status code
+ * a12. send a 4-byte int to
+ * ack the receive of the
+ * final status code
+ * c13. receive and exit
+ *
+ * b14. destroy the socket
+ *
+ * Note that a12 and c13 are necessary to ensure a11 happens before the socket
+ * gets destroyed in b14.
+ */
+
+#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -49,6 +96,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
+#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -63,6 +111,7 @@
#include <android-base/strings.h>
#include <cutils/android_reboot.h>
#include <cutils/properties.h>
+#include <cutils/sockets.h>
#include <fs_mgr.h>
#define LOG_TAG "uncrypt"
@@ -73,12 +122,21 @@
#define WINDOW_SIZE 5
+// uncrypt provides three services: SETUP_BCB, CLEAR_BCB and UNCRYPT.
+//
+// SETUP_BCB and CLEAR_BCB services use socket communication and do not rely
+// on /cache partitions. They will handle requests to reboot into recovery
+// (for applying updates for non-A/B devices, or factory resets for all
+// devices).
+//
+// UNCRYPT service still needs files on /cache partition (UNCRYPT_PATH_FILE
+// and CACHE_BLOCK_MAP). It will be working (and needed) only for non-A/B
+// devices, on which /cache partitions always exist.
static const std::string CACHE_BLOCK_MAP = "/cache/recovery/block.map";
-static const std::string COMMAND_FILE = "/cache/recovery/command";
-static const std::string STATUS_FILE = "/cache/recovery/uncrypt_status";
static const std::string UNCRYPT_PATH_FILE = "/cache/recovery/uncrypt_file";
+static const std::string UNCRYPT_SOCKET = "uncrypt";
-static struct fstab* fstab = NULL;
+static struct fstab* fstab = nullptr;
static int write_at_offset(unsigned char* buffer, size_t size, int wfd, off64_t offset) {
if (TEMP_FAILURE_RETRY(lseek64(wfd, offset, SEEK_SET)) == -1) {
@@ -152,6 +210,11 @@ static const char* find_block_device(const char* path, bool* encryptable, bool*
return NULL;
}
+static bool write_status_to_socket(int status, int socket) {
+ int status_out = htonl(status);
+ return android::base::WriteFully(socket, &status_out, sizeof(int));
+}
+
// Parse uncrypt_file to find the update package name.
static bool find_uncrypt_package(const std::string& uncrypt_path_file, std::string* package_name) {
CHECK(package_name != nullptr);
@@ -167,7 +230,7 @@ static bool find_uncrypt_package(const std::string& uncrypt_path_file, std::stri
}
static int produce_block_map(const char* path, const char* map_file, const char* blk_dev,
- bool encrypted, int status_fd) {
+ bool encrypted, int socket) {
std::string err;
if (!android::base::RemoveFileIfExists(map_file, &err)) {
ALOGE("failed to remove the existing map file %s: %s", map_file, err.c_str());
@@ -180,9 +243,9 @@ static int produce_block_map(const char* path, const char* map_file, const char*
return -1;
}
- // 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());
+ // Make sure we can write to the socket.
+ if (!write_status_to_socket(0, socket)) {
+ ALOGE("failed to write to socket %d\n", socket);
return -1;
}
@@ -234,8 +297,8 @@ static int produce_block_map(const char* path, const char* map_file, const char*
// 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);
+ last_progress = progress;
+ write_status_to_socket(progress, socket);
}
if ((tail+1) % WINDOW_SIZE == head) {
@@ -352,15 +415,12 @@ static int produce_block_map(const char* path, const char* map_file, const char*
}
static std::string get_misc_blk_device() {
- struct fstab* fstab = read_fstab();
if (fstab == nullptr) {
return "";
}
- for (int i = 0; i < fstab->num_entries; ++i) {
- fstab_rec* v = &fstab->recs[i];
- if (v->mount_point != nullptr && strcmp(v->mount_point, "/misc") == 0) {
- return v->blk_device;
- }
+ struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
+ if (rec != nullptr) {
+ return rec->blk_device;
}
return "";
}
@@ -406,8 +466,7 @@ static int write_bootloader_message(const bootloader_message* in) {
return 0;
}
-static int uncrypt(const char* input_path, const char* map_file, int status_fd) {
-
+static int uncrypt(const char* input_path, const char* map_file, const int socket) {
ALOGI("update package is \"%s\"", input_path);
// Turn the name of the file we're supposed to convert into an
@@ -418,10 +477,6 @@ static int uncrypt(const char* input_path, const char* map_file, int status_fd)
return 1;
}
- if (read_fstab() == NULL) {
- return 1;
- }
-
bool encryptable;
bool encrypted;
const char* blk_dev = find_block_device(path, &encryptable, &encrypted);
@@ -445,7 +500,7 @@ static int uncrypt(const char* input_path, const char* map_file, int status_fd)
// and /sdcard we leave the file alone.
if (strncmp(path, "/data/", 6) == 0) {
ALOGI("writing block map %s", map_file);
- if (produce_block_map(path, map_file, blk_dev, encrypted, status_fd) != 0) {
+ if (produce_block_map(path, map_file, blk_dev, encrypted, socket) != 0) {
return 1;
}
}
@@ -453,71 +508,66 @@ static int uncrypt(const char* input_path, const char* map_file, int status_fd)
return 0;
}
-static int uncrypt_wrapper(const char* input_path, const char* map_file,
- const std::string& status_file) {
- // The pipe has been created by the system server.
- unique_fd status_fd(open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR));
- if (!status_fd) {
- ALOGE("failed to open pipe \"%s\": %s", status_file.c_str(), strerror(errno));
- return 1;
- }
-
+static bool uncrypt_wrapper(const char* input_path, const char* map_file, const int socket) {
std::string package;
if (input_path == nullptr) {
if (!find_uncrypt_package(UNCRYPT_PATH_FILE, &package)) {
- android::base::WriteStringToFd("-1\n", status_fd.get());
- return 1;
+ write_status_to_socket(-1, socket);
+ return false;
}
input_path = package.c_str();
}
CHECK(map_file != nullptr);
- int status = uncrypt(input_path, map_file, status_fd.get());
+ int status = uncrypt(input_path, map_file, socket);
if (status != 0) {
- android::base::WriteStringToFd("-1\n", status_fd.get());
- return 1;
+ write_status_to_socket(-1, socket);
+ return false;
}
- android::base::WriteStringToFd("100\n", status_fd.get());
- return 0;
+ write_status_to_socket(100, socket);
+ return true;
}
-static int clear_bcb(const std::string& status_file) {
- unique_fd status_fd(open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR));
- if (!status_fd) {
- ALOGE("failed to open pipe \"%s\": %s", status_file.c_str(), strerror(errno));
- return 1;
- }
+static bool clear_bcb(const int socket) {
bootloader_message boot = {};
if (write_bootloader_message(&boot) != 0) {
- android::base::WriteStringToFd("-1\n", status_fd.get());
- return 1;
+ write_status_to_socket(-1, socket);
+ return false;
}
- android::base::WriteStringToFd("100\n", status_fd.get());
- return 0;
+ write_status_to_socket(100, socket);
+ return true;
}
-static int setup_bcb(const std::string& command_file, const std::string& status_file) {
- unique_fd status_fd(open(status_file.c_str(), O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR));
- if (!status_fd) {
- ALOGE("failed to open pipe \"%s\": %s", status_file.c_str(), strerror(errno));
- return 1;
+static bool setup_bcb(const int socket) {
+ // c5. receive message length
+ int length;
+ if (!android::base::ReadFully(socket, &length, 4)) {
+ ALOGE("failed to read the length: %s", strerror(errno));
+ return false;
}
+ length = ntohl(length);
+
+ // c7. receive message
std::string content;
- if (!android::base::ReadFileToString(command_file, &content)) {
- ALOGE("failed to read \"%s\": %s", command_file.c_str(), strerror(errno));
- android::base::WriteStringToFd("-1\n", status_fd.get());
- return 1;
+ content.resize(length);
+ if (!android::base::ReadFully(socket, &content[0], length)) {
+ ALOGE("failed to read the length: %s", strerror(errno));
+ return false;
}
+ ALOGI(" received command: [%s] (%zu)", content.c_str(), content.size());
+
+ // c8. setup the bcb command
bootloader_message boot = {};
strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
strlcat(boot.recovery, content.c_str(), sizeof(boot.recovery));
if (write_bootloader_message(&boot) != 0) {
ALOGE("failed to set bootloader message");
- android::base::WriteStringToFd("-1\n", status_fd.get());
- return 1;
+ write_status_to_socket(-1, socket);
+ return false;
}
- android::base::WriteStringToFd("100\n", status_fd.get());
- return 0;
+ // c10. send "100" status
+ write_status_to_socket(100, socket);
+ return true;
}
static int read_bcb() {
@@ -540,23 +590,75 @@ static void usage(const char* exename) {
}
int main(int argc, char** argv) {
- if (argc == 2) {
- if (strcmp(argv[1], "--clear-bcb") == 0) {
- return clear_bcb(STATUS_FILE);
- } else if (strcmp(argv[1], "--setup-bcb") == 0) {
- return setup_bcb(COMMAND_FILE, STATUS_FILE);
- } else if (strcmp(argv[1], "--read-bcb") == 0) {
- return read_bcb();
- }
- } else if (argc == 1 || argc == 3) {
- const char* input_path = nullptr;
- const char* map_file = CACHE_BLOCK_MAP.c_str();
- if (argc == 3) {
- input_path = argv[1];
- map_file = argv[2];
- }
- return uncrypt_wrapper(input_path, map_file, STATUS_FILE);
+ enum { UNCRYPT, SETUP_BCB, CLEAR_BCB } action;
+ const char* input_path = nullptr;
+ const char* map_file = CACHE_BLOCK_MAP.c_str();
+
+ if (argc == 2 && strcmp(argv[1], "--clear-bcb") == 0) {
+ action = CLEAR_BCB;
+ } else if (argc == 2 && strcmp(argv[1], "--setup-bcb") == 0) {
+ action = SETUP_BCB;
+ } else if (argc ==2 && strcmp(argv[1], "--read-bcb") == 0) {
+ return read_bcb();
+ } else if (argc == 1) {
+ action = UNCRYPT;
+ } else if (argc == 3) {
+ input_path = argv[1];
+ map_file = argv[2];
+ action = UNCRYPT;
+ } else {
+ usage(argv[0]);
+ return 2;
+ }
+
+ if ((fstab = read_fstab()) == nullptr) {
+ return 1;
+ }
+
+ // c3. The socket is created by init when starting the service. uncrypt
+ // will use the socket to communicate with its caller.
+ unique_fd service_socket(android_get_control_socket(UNCRYPT_SOCKET.c_str()));
+ if (!service_socket) {
+ ALOGE("failed to open socket \"%s\": %s", UNCRYPT_SOCKET.c_str(), strerror(errno));
+ return 1;
+ }
+ fcntl(service_socket.get(), F_SETFD, FD_CLOEXEC);
+
+ if (listen(service_socket.get(), 1) == -1) {
+ ALOGE("failed to listen on socket %d: %s", service_socket.get(), strerror(errno));
+ return 1;
+ }
+
+ unique_fd socket_fd(accept4(service_socket.get(), nullptr, nullptr, SOCK_CLOEXEC));
+ if (!socket_fd) {
+ ALOGE("failed to accept on socket %d: %s", service_socket.get(), strerror(errno));
+ return 1;
+ }
+
+ bool success = false;
+ switch (action) {
+ case UNCRYPT:
+ success = uncrypt_wrapper(input_path, map_file, socket_fd.get());
+ break;
+ case SETUP_BCB:
+ success = setup_bcb(socket_fd.get());
+ break;
+ case CLEAR_BCB:
+ success = clear_bcb(socket_fd.get());
+ break;
+ default: // Should never happen.
+ ALOGE("Invalid uncrypt action code: %d", action);
+ return 1;
+ }
+
+ // c13. Read a 4-byte code from the client before uncrypt exits. This is to
+ // ensure the client to receive the last status code before the socket gets
+ // destroyed.
+ int code;
+ if (android::base::ReadFully(socket_fd.get(), &code, 4)) {
+ ALOGI(" received %d, exiting now", code);
+ } else {
+ ALOGE("failed to read the code: %s", strerror(errno));
}
- usage(argv[0]);
- return 2;
+ return success ? 0 : 1;
}
diff --git a/uncrypt/uncrypt.rc b/uncrypt/uncrypt.rc
index d5d803b9f..52f564eb6 100644
--- a/uncrypt/uncrypt.rc
+++ b/uncrypt/uncrypt.rc
@@ -1,14 +1,17 @@
service uncrypt /system/bin/uncrypt
class main
+ socket uncrypt stream 600 system system
disabled
oneshot
service setup-bcb /system/bin/uncrypt --setup-bcb
class main
+ socket uncrypt stream 600 system system
disabled
oneshot
service clear-bcb /system/bin/uncrypt --clear-bcb
class main
+ socket uncrypt stream 600 system system
disabled
oneshot