diff options
Diffstat (limited to '')
-rw-r--r-- | uncrypt/Android.mk | 19 | ||||
-rw-r--r-- | uncrypt/bootloader_message_writer.cpp | 107 | ||||
-rw-r--r-- | uncrypt/include/bootloader_message_writer.h | 35 | ||||
-rw-r--r-- | uncrypt/uncrypt.cpp | 533 | ||||
-rw-r--r-- | uncrypt/uncrypt.rc | 17 |
5 files changed, 509 insertions, 202 deletions
diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index bd769f900..0a30b49f0 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -15,13 +15,30 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) +LOCAL_CLANG := true +LOCAL_SRC_FILES := bootloader_message_writer.cpp +LOCAL_MODULE := libbootloader_message_writer +LOCAL_STATIC_LIBRARIES := libbase libfs_mgr +LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_CLANG := true LOCAL_C_INCLUDES += $(commands_recovery_local_path) LOCAL_SRC_FILES := uncrypt.cpp LOCAL_MODULE_TAGS := optional + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. + LOCAL_MODULE := uncrypt -LOCAL_STATIC_LIBRARIES := libbase liblog libfs_mgr libcutils +LOCAL_STATIC_LIBRARIES := libbootloader_message_writer libbase \ + liblog libfs_mgr libcutils \ + +LOCAL_INIT_RC := uncrypt.rc include $(BUILD_EXECUTABLE) diff --git a/uncrypt/bootloader_message_writer.cpp b/uncrypt/bootloader_message_writer.cpp new file mode 100644 index 000000000..3bb106aa0 --- /dev/null +++ b/uncrypt/bootloader_message_writer.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2016 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 <errno.h> +#include <fcntl.h> +#include <string.h> +#include <sys/system_properties.h> + +#include <string> +#include <vector> + +#include <android-base/file.h> +#include <android-base/stringprintf.h> +#include <android-base/unique_fd.h> +#include <fs_mgr.h> + +#include "bootloader.h" + +static struct fstab* read_fstab(std::string* err) { + // The fstab path is always "/fstab.${ro.hardware}". + std::string fstab_path = "/fstab."; + char value[PROP_VALUE_MAX]; + if (__system_property_get("ro.hardware", value) == 0) { + *err = "failed to get ro.hardware"; + return nullptr; + } + fstab_path += value; + struct fstab* fstab = fs_mgr_read_fstab(fstab_path.c_str()); + if (fstab == nullptr) { + *err = "failed to read " + fstab_path; + } + return fstab; +} + +static std::string get_misc_blk_device(std::string* err) { + struct fstab* fstab = read_fstab(err); + if (fstab == nullptr) { + return ""; + } + fstab_rec* record = fs_mgr_get_entry_for_mount_point(fstab, "/misc"); + if (record == nullptr) { + *err = "failed to find /misc partition"; + return ""; + } + return record->blk_device; +} + +static bool write_bootloader_message(const bootloader_message& boot, std::string* err) { + std::string misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY | O_SYNC)); + if (fd.get() == -1) { + *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(), + strerror(errno)); + return false; + } + if (!android::base::WriteFully(fd.get(), &boot, sizeof(boot))) { + *err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(), + strerror(errno)); + return false; + } + // TODO: O_SYNC and fsync duplicates each other? + if (fsync(fd.get()) == -1) { + *err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(), + strerror(errno)); + return false; + } + return true; +} + +bool clear_bootloader_message(std::string* err) { + bootloader_message boot = {}; + return write_bootloader_message(boot, err); +} + +bool write_bootloader_message(const std::vector<std::string>& options, std::string* err) { + bootloader_message boot = {}; + strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); + strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery)); + for (const auto& s : options) { + strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery)); + if (s.back() != '\n') { + strlcat(boot.recovery, "\n", sizeof(boot.recovery)); + } + } + return write_bootloader_message(boot, err); +} + +extern "C" bool write_bootloader_message(const char* options) { + std::string err; + return write_bootloader_message({options}, &err); +} diff --git a/uncrypt/include/bootloader_message_writer.h b/uncrypt/include/bootloader_message_writer.h new file mode 100644 index 000000000..e0ca3f44a --- /dev/null +++ b/uncrypt/include/bootloader_message_writer.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 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 BOOTLOADER_MESSAGE_WRITER_H +#define BOOTLOADER_MESSAGE_WRITER_H + +#ifdef __cplusplus +#include <string> +#include <vector> + +bool clear_bootloader_message(std::string* err); + +bool write_bootloader_message(const std::vector<std::string>& options, std::string* err); + +#else +#include <stdbool.h> + +// C Interface. +bool write_bootloader_message(const char* options); +#endif + +#endif // BOOTLOADER_MESSAGE_WRITER_H diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 1db3013c6..d7105a01f 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -39,75 +39,126 @@ // 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> +#include <libgen.h> #include <linux/fs.h> #include <stdarg.h> #include <stdio.h> #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> -#include <base/file.h> -#include <base/strings.h> +#include <algorithm> +#include <memory> +#include <vector> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> +#include <bootloader_message_writer.h> +#include <cutils/android_reboot.h> #include <cutils/properties.h> +#include <cutils/sockets.h> #include <fs_mgr.h> + #define LOG_TAG "uncrypt" #include <log/log.h> +#include "unique_fd.h" + #define WINDOW_SIZE 5 -static const std::string 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"; +// 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 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) { - ALOGE("error seeking to offset %lld: %s\n", offset, strerror(errno)); + ALOGE("error seeking to offset %" PRId64 ": %s", offset, strerror(errno)); return -1; } - size_t written = 0; - while (written < size) { - ssize_t wrote = TEMP_FAILURE_RETRY(write(wfd, buffer + written, size - written)); - if (wrote == -1) { - ALOGE("error writing offset %lld: %s\n", (offset + written), strerror(errno)); - return -1; - } - written += wrote; + if (!android::base::WriteFully(wfd, buffer, size)) { + ALOGE("error writing offset %" PRId64 ": %s", offset, strerror(errno)); + return -1; } return 0; } -static void add_block_to_ranges(int** ranges, int* range_alloc, int* range_used, int new_block) { - // If the current block start is < 0, set the start to the new - // block. (This only happens for the very first block of the very - // first range.) - if ((*ranges)[*range_used*2-2] < 0) { - (*ranges)[*range_used*2-2] = new_block; - (*ranges)[*range_used*2-1] = new_block; - } - - if (new_block == (*ranges)[*range_used*2-1]) { +static void add_block_to_ranges(std::vector<int>& ranges, int new_block) { + if (!ranges.empty() && new_block == ranges.back()) { // If the new block comes immediately after the current range, // all we have to do is extend the current range. - ++(*ranges)[*range_used*2-1]; + ++ranges.back(); } else { // We need to start a new range. - - // If there isn't enough room in the array, we need to expand it. - if (*range_used >= *range_alloc) { - *range_alloc *= 2; - *ranges = reinterpret_cast<int*>(realloc(*ranges, *range_alloc * 2 * sizeof(int))); - } - - ++*range_used; - (*ranges)[*range_used*2-2] = new_block; - (*ranges)[*range_used*2-1] = new_block+1; + ranges.push_back(new_block); + ranges.push_back(new_block + 1); } } @@ -117,13 +168,13 @@ static struct fstab* read_fstab() { // The fstab path is always "/fstab.${ro.hardware}". char fstab_path[PATH_MAX+1] = "/fstab."; if (!property_get("ro.hardware", fstab_path+strlen(fstab_path), "")) { - ALOGE("failed to get ro.hardware\n"); + ALOGE("failed to get ro.hardware"); return NULL; } fstab = fs_mgr_read_fstab(fstab_path); if (!fstab) { - ALOGE("failed to read %s\n", fstab_path); + ALOGE("failed to read %s", fstab_path); return NULL; } @@ -144,7 +195,7 @@ static const char* find_block_device(const char* path, bool* encryptable, bool* (path[len] == '/' || path[len] == 0)) { *encrypted = false; *encryptable = false; - if (fs_mgr_is_encryptable(v)) { + if (fs_mgr_is_encryptable(v) || fs_mgr_is_file_encrypted(v)) { *encryptable = true; char buffer[PROPERTY_VALUE_MAX+1]; if (property_get("ro.crypto.state", buffer, "") && @@ -159,101 +210,108 @@ 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(std::string& package_name) -{ - if (!android::base::ReadFileToString(uncrypt_file, &package_name)) { - ALOGE("failed to open \"%s\": %s\n", uncrypt_file.c_str(), strerror(errno)); +static bool find_uncrypt_package(const std::string& uncrypt_path_file, std::string* package_name) { + CHECK(package_name != nullptr); + std::string uncrypt_path; + if (!android::base::ReadFileToString(uncrypt_path_file, &uncrypt_path)) { + ALOGE("failed to open \"%s\": %s", uncrypt_path_file.c_str(), strerror(errno)); return false; } // Remove the trailing '\n' if present. - package_name = android::base::Trim(package_name); - + *package_name = android::base::Trim(uncrypt_path); return true; } static int produce_block_map(const char* path, const char* map_file, const char* blk_dev, - bool encrypted, int status_fd) { - int mapfd = open(map_file, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR); - if (mapfd == -1) { - ALOGE("failed to open %s\n", map_file); + 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()); + return -1; + } + std::string tmp_map_file = std::string(map_file) + ".tmp"; + unique_fd mapfd(open(tmp_map_file.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)); + if (!mapfd) { + ALOGE("failed to open %s: %s\n", tmp_map_file.c_str(), strerror(errno)); return -1; } - 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()); + // 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; } struct stat sb; - int ret = stat(path, &sb); - if (ret != 0) { - ALOGE("failed to stat %s\n", path); + if (stat(path, &sb) != 0) { + ALOGE("failed to stat %s", path); return -1; } - ALOGI(" block size: %ld bytes\n", (long)sb.st_blksize); + ALOGI(" block size: %ld bytes", static_cast<long>(sb.st_blksize)); int blocks = ((sb.st_size-1) / sb.st_blksize) + 1; - ALOGI(" file size: %lld bytes, %d blocks\n", (long long)sb.st_size, blocks); + ALOGI(" file size: %" PRId64 " bytes, %d blocks", sb.st_size, blocks); - int range_alloc = 1; - int range_used = 1; - int* ranges = reinterpret_cast<int*>(malloc(range_alloc * 2 * sizeof(int))); - ranges[0] = -1; - ranges[1] = -1; + std::vector<int> ranges; - fprintf(mapf, "%s\n%lld %lu\n", blk_dev, (long long)sb.st_size, (unsigned long)sb.st_blksize); + std::string s = android::base::StringPrintf("%s\n%" PRId64 " %ld\n", + blk_dev, sb.st_size, static_cast<long>(sb.st_blksize)); + if (!android::base::WriteStringToFd(s, mapfd.get())) { + ALOGE("failed to write %s: %s", tmp_map_file.c_str(), strerror(errno)); + return -1; + } - unsigned char* buffers[WINDOW_SIZE]; + std::vector<std::vector<unsigned char>> buffers; if (encrypted) { - for (size_t i = 0; i < WINDOW_SIZE; ++i) { - buffers[i] = reinterpret_cast<unsigned char*>(malloc(sb.st_blksize)); - } + buffers.resize(WINDOW_SIZE, std::vector<unsigned char>(sb.st_blksize)); } int head_block = 0; int head = 0, tail = 0; - size_t pos = 0; - int fd = open(path, O_RDONLY); - if (fd < 0) { - ALOGE("failed to open fd for reading: %s\n", strerror(errno)); + unique_fd fd(open(path, O_RDONLY)); + if (!fd) { + ALOGE("failed to open %s for reading: %s", path, strerror(errno)); return -1; } - int wfd = -1; + unique_fd wfd(-1); if (encrypted) { - wfd = open(blk_dev, O_WRONLY | O_SYNC); - if (wfd < 0) { - ALOGE("failed to open fd for writing: %s\n", strerror(errno)); + wfd = open(blk_dev, O_WRONLY); + if (!wfd) { + ALOGE("failed to open fd for writing: %s", strerror(errno)); return -1; } } + off64_t pos = 0; int last_progress = 0; 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); + last_progress = progress; + write_status_to_socket(progress, socket); } if ((tail+1) % WINDOW_SIZE == head) { // write out head buffer int block = head_block; - ret = ioctl(fd, FIBMAP, &block); - if (ret != 0) { - ALOGE("failed to find block %d\n", head_block); + if (ioctl(fd.get(), FIBMAP, &block) != 0) { + ALOGE("failed to find block %d", head_block); return -1; } - add_block_to_ranges(&ranges, &range_alloc, &range_used, block); + add_block_to_ranges(ranges, block); if (encrypted) { - if (write_at_offset(buffers[head], sb.st_blksize, wfd, - (off64_t)sb.st_blksize * block) != 0) { + if (write_at_offset(buffers[head].data(), sb.st_blksize, wfd.get(), + static_cast<off64_t>(sb.st_blksize) * block) != 0) { return -1; } } @@ -263,17 +321,13 @@ static int produce_block_map(const char* path, const char* map_file, const char* // read next block to tail if (encrypted) { - size_t so_far = 0; - while (so_far < sb.st_blksize && pos < sb.st_size) { - ssize_t this_read = - TEMP_FAILURE_RETRY(read(fd, buffers[tail] + so_far, sb.st_blksize - so_far)); - if (this_read == -1) { - ALOGE("failed to read: %s\n", strerror(errno)); - return -1; - } - so_far += this_read; - pos += this_read; + size_t to_read = static_cast<size_t>( + std::min(static_cast<off64_t>(sb.st_blksize), sb.st_size - pos)); + if (!android::base::ReadFully(fd.get(), buffers[tail].data(), to_read)) { + ALOGE("failed to read: %s", strerror(errno)); + return -1; } + pos += to_read; } else { // If we're not encrypting; we don't need to actually read // anything, just skip pos forward as if we'd read a @@ -286,15 +340,14 @@ static int produce_block_map(const char* path, const char* map_file, const char* while (head != tail) { // write out head buffer int block = head_block; - ret = ioctl(fd, FIBMAP, &block); - if (ret != 0) { - ALOGE("failed to find block %d\n", head_block); + if (ioctl(fd.get(), FIBMAP, &block) != 0) { + ALOGE("failed to find block %d", head_block); return -1; } - add_block_to_ranges(&ranges, &range_alloc, &range_used, block); + add_block_to_ranges(ranges, block); if (encrypted) { - if (write_at_offset(buffers[head], sb.st_blksize, wfd, - (off64_t)sb.st_blksize * block) != 0) { + if (write_at_offset(buffers[head].data(), sb.st_blksize, wfd.get(), + static_cast<off64_t>(sb.st_blksize) * block) != 0) { return -1; } } @@ -302,68 +355,66 @@ static int produce_block_map(const char* path, const char* map_file, const char* ++head_block; } - fprintf(mapf, "%d\n", range_used); - for (int i = 0; i < range_used; ++i) { - fprintf(mapf, "%d %d\n", ranges[i*2], ranges[i*2+1]); - } - - if (fsync(mapfd) == -1) { - ALOGE("failed to fsync \"%s\": %s\n", map_file, strerror(errno)); + if (!android::base::WriteStringToFd( + android::base::StringPrintf("%zu\n", ranges.size() / 2), mapfd.get())) { + ALOGE("failed to write %s: %s", tmp_map_file.c_str(), strerror(errno)); return -1; } - fclose(mapf); - close(fd); - if (encrypted) { - if (fsync(wfd) == -1) { - ALOGE("failed to fsync \"%s\": %s\n", blk_dev, strerror(errno)); + for (size_t i = 0; i < ranges.size(); i += 2) { + if (!android::base::WriteStringToFd( + android::base::StringPrintf("%d %d\n", ranges[i], ranges[i+1]), mapfd.get())) { + ALOGE("failed to write %s: %s", tmp_map_file.c_str(), strerror(errno)); return -1; } - close(wfd); } - return 0; -} + if (fsync(mapfd.get()) == -1) { + ALOGE("failed to fsync \"%s\": %s", tmp_map_file.c_str(), strerror(errno)); + return -1; + } + if (close(mapfd.get()) == -1) { + ALOGE("failed to close %s: %s", tmp_map_file.c_str(), strerror(errno)); + return -1; + } + mapfd = -1; -static void wipe_misc() { - ALOGI("removing old commands from misc"); - for (int i = 0; i < fstab->num_entries; ++i) { - struct fstab_rec* v = &fstab->recs[i]; - if (!v->mount_point) continue; - if (strcmp(v->mount_point, "/misc") == 0) { - int fd = open(v->blk_device, O_WRONLY | O_SYNC); - uint8_t zeroes[1088]; // sizeof(bootloader_message) from recovery - memset(zeroes, 0, sizeof(zeroes)); - - size_t written = 0; - size_t size = sizeof(zeroes); - while (written < size) { - ssize_t w = TEMP_FAILURE_RETRY(write(fd, zeroes, size-written)); - if (w == -1) { - ALOGE("zero write failed: %s\n", strerror(errno)); - return; - } else { - written += w; - } - } - if (fsync(fd) == -1) { - ALOGE("failed to fsync \"%s\": %s\n", v->blk_device, strerror(errno)); - close(fd); - return; - } - close(fd); + if (encrypted) { + if (fsync(wfd.get()) == -1) { + ALOGE("failed to fsync \"%s\": %s", blk_dev, strerror(errno)); + return -1; + } + if (close(wfd.get()) == -1) { + ALOGE("failed to close %s: %s", blk_dev, strerror(errno)); + return -1; } + wfd = -1; } -} -static void reboot_to_recovery() { - ALOGI("rebooting to recovery"); - property_set("sys.powerctl", "reboot,recovery"); - sleep(10); - ALOGE("reboot didn't succeed?"); + if (rename(tmp_map_file.c_str(), map_file) == -1) { + ALOGE("failed to rename %s to %s: %s", tmp_map_file.c_str(), map_file, strerror(errno)); + return -1; + } + // Sync dir to make rename() result written to disk. + std::string file_name = map_file; + std::string dir_name = dirname(&file_name[0]); + unique_fd dfd(open(dir_name.c_str(), O_RDONLY | O_DIRECTORY)); + if (!dfd) { + ALOGE("failed to open dir %s: %s", dir_name.c_str(), strerror(errno)); + return -1; + } + if (fsync(dfd.get()) == -1) { + ALOGE("failed to fsync %s: %s", dir_name.c_str(), strerror(errno)); + return -1; + } + if (close(dfd.get()) == -1) { + ALOGE("failed to close %s: %s", dir_name.c_str(), strerror(errno)); + return -1; + } + dfd = -1; + return 0; } -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 @@ -374,10 +425,6 @@ 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); @@ -389,8 +436,8 @@ int uncrypt(const char* input_path, const char* map_file, int status_fd) { // If the filesystem it's on isn't encrypted, we only produce the // block map, we don't rewrite the file contents (it would be // pointless to do so). - ALOGI("encryptable: %s\n", encryptable ? "yes" : "no"); - ALOGI(" encrypted: %s\n", encrypted ? "yes" : "no"); + ALOGI("encryptable: %s", encryptable ? "yes" : "no"); + ALOGI(" encrypted: %s", encrypted ? "yes" : "no"); // Recovery supports installing packages from 3 paths: /cache, // /data, and /sdcard. (On a particular device, other locations @@ -401,7 +448,7 @@ 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; } } @@ -409,57 +456,141 @@ int uncrypt(const char* input_path, const char* map_file, int status_fd) { return 0; } -int main(int argc, char** argv) { - const char* input_path; - const char* map_file; +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)) { + write_status_to_socket(-1, socket); + return false; + } + input_path = package.c_str(); + } + CHECK(map_file != nullptr); + int status = uncrypt(input_path, map_file, socket); + if (status != 0) { + write_status_to_socket(-1, socket); + return false; + } + write_status_to_socket(100, socket); + return true; +} - 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; +static bool clear_bcb(const int socket) { + std::string err; + if (!clear_bootloader_message(&err)) { + ALOGE("failed to clear bootloader message: %s", err.c_str()); + write_status_to_socket(-1, socket); + return false; } + write_status_to_socket(100, socket); + return true; +} - // 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; - } - wipe_misc(); - reboot_to_recovery(); +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; + 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 + std::string err; + if (!write_bootloader_message({content}, &err)) { + ALOGE("failed to set bootloader message: %s", err.c_str()); + write_status_to_socket(-1, socket); + return false; + } + // c10. send "100" status + write_status_to_socket(100, socket); + return true; +} + +static void usage(const char* exename) { + fprintf(stderr, "Usage of %s:\n", exename); + fprintf(stderr, "%s [<package_path> <map_file>] Uncrypt ota package.\n", exename); + fprintf(stderr, "%s --clear-bcb Clear BCB data in misc partition.\n", exename); + fprintf(stderr, "%s --setup-bcb Setup BCB data by command file.\n", exename); +} + +int main(int argc, char** argv) { + 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 == 1) { + action = UNCRYPT; + } else if (argc == 3) { + input_path = argv[1]; + map_file = argv[2]; + action = UNCRYPT; } else { - // The pipe has been created by the system server. - 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; - } + usage(argv[0]); + return 2; + } - 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 { - std::string package; - if (!find_uncrypt_package(package)) { - android::base::WriteStringToFd("-1\n", status_fd); - close(status_fd); - return 1; - } - input_path = package.c_str(); - map_file = cache_block_map.c_str(); - } + if ((fstab = read_fstab()) == nullptr) { + 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; - } + // 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); - android::base::WriteStringToFd("100\n", status_fd); - close(status_fd); + if (listen(service_socket.get(), 1) == -1) { + ALOGE("failed to listen on socket %d: %s", service_socket.get(), strerror(errno)); + return 1; } - return 0; + 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)); + } + return success ? 0 : 1; } diff --git a/uncrypt/uncrypt.rc b/uncrypt/uncrypt.rc new file mode 100644 index 000000000..52f564eb6 --- /dev/null +++ b/uncrypt/uncrypt.rc @@ -0,0 +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 |