From 7bad7c4646ee8fd8d6e6ed0ffd3ddbb0c1b41a2f Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Tue, 28 Apr 2015 17:24:24 -0700 Subject: Check all lseek calls succeed. Also add missing TEMP_FAILURE_RETRYs on read, write, and lseek. Bug: http://b/20625546 Change-Id: I03b198e11c1921b35518ee2dd005a7cfcf4fd94b --- uncrypt/uncrypt.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'uncrypt') diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c index aa75210b0..da035dfba 100644 --- a/uncrypt/uncrypt.c +++ b/uncrypt/uncrypt.c @@ -65,12 +65,15 @@ static struct fstab* fstab = NULL; static int write_at_offset(unsigned char* buffer, size_t size, int wfd, off64_t offset) { - lseek64(wfd, offset, SEEK_SET); + if (TEMP_FAILURE_RETRY(lseek64(wfd, offset, SEEK_SET)) == -1) { + ALOGE("error seeking to offset %lld: %s\n", offset, strerror(errno)); + return -1; + } size_t written = 0; while (written < size) { - ssize_t wrote = write(wfd, buffer + written, size - written); - if (wrote < 0) { - ALOGE("error writing offset %lld: %s\n", offset, strerror(errno)); + 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; @@ -275,8 +278,9 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de if (encrypted) { size_t so_far = 0; while (so_far < sb.st_blksize && pos < sb.st_size) { - ssize_t this_read = read(fd, buffers[tail] + so_far, sb.st_blksize - so_far); - if (this_read < 0) { + 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; } @@ -340,8 +344,8 @@ void wipe_misc() { size_t written = 0; size_t size = sizeof(zeroes); while (written < size) { - ssize_t w = write(fd, zeroes, size-written); - if (w < 0 && errno != EINTR) { + ssize_t w = TEMP_FAILURE_RETRY(write(fd, zeroes, size-written)); + if (w == -1) { ALOGE("zero write failed: %s\n", strerror(errno)); return; } else { -- cgit v1.2.3 From fb4ccef1df4f0bd8fa830c750f2970dd2df9e51b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 4 May 2015 10:10:13 -0700 Subject: uncrypt: package on non-data partition should follow the right path Fix the accidental change of behavior in [1]. OTA packages not on /data partition should still go through the path that has validity checks and wipe_misc() steps. [1]: commit eaf33654c1817bd665831a13c5bd0c04daabee02. Change-Id: Ice9a049f6259cd2368d2fb95a991f8a6a0120bdd --- uncrypt/uncrypt.c | 61 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 20 deletions(-) (limited to 'uncrypt') diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c index da035dfba..42ae649cc 100644 --- a/uncrypt/uncrypt.c +++ b/uncrypt/uncrypt.c @@ -159,12 +159,13 @@ const char* find_block_device(const char* path, int* encryptable, int* encrypted return NULL; } -char* parse_recovery_command_file() +// Parse the command file RECOVERY_COMMAND_FILE to find the update package +// name. If it's on the /data partition, replace the package name with the +// 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() { - char* fn = NULL; - int count = 0; - char temp[1024]; - FILE* f = fopen(RECOVERY_COMMAND_FILE, "r"); if (f == NULL) { return NULL; @@ -175,17 +176,27 @@ char* parse_recovery_command_file() return NULL; } FILE* fo = fdopen(fd, "w"); - - while (fgets(temp, sizeof(temp), f)) { - printf("read: %s", temp); - if (strncmp(temp, "--update_package=/data/", strlen("--update_package=/data/")) == 0) { - fn = strdup(temp + strlen("--update_package=")); - strcpy(temp, "--update_package=@" CACHE_BLOCK_MAP "\n"); + 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(temp, fo); + fputs(line, fo); } + free(line); fclose(f); - fsync(fd); + 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) { @@ -244,7 +255,6 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de ALOGE("failed to open fd for reading: %s\n", strerror(errno)); return -1; } - fsync(fd); int wfd = -1; if (encrypted) { @@ -319,11 +329,17 @@ int produce_block_map(const char* path, const char* map_file, const char* blk_de fprintf(mapf, "%d %d\n", ranges[i*2], ranges[i*2+1]); } - fsync(mapfd); + if (fsync(mapfd) == -1) { + ALOGE("failed to fsync \"%s\": %s\n", map_file, strerror(errno)); + return -1; + } fclose(mapf); close(fd); if (encrypted) { - fsync(wfd); + if (fsync(wfd) == -1) { + ALOGE("failed to fsync \"%s\": %s\n", blk_dev, strerror(errno)); + return -1; + } close(wfd); } @@ -352,7 +368,11 @@ void wipe_misc() { written += w; } } - fsync(fd); + if (fsync(fd) == -1) { + ALOGE("failed to fsync \"%s\": %s\n", v->blk_device, strerror(errno)); + close(fd); + return; + } close(fd); } } @@ -383,7 +403,7 @@ int main(int argc, char** argv) map_file = argv[2]; do_reboot = 0; } else { - input_path = parse_recovery_command_file(); + 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 @@ -432,15 +452,16 @@ int main(int argc, char** argv) 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); if (produce_block_map(path, map_file, blk_dev, encrypted) != 0) { return 1; } + wipe_misc(); + rename(RECOVERY_COMMAND_FILE_TMP, RECOVERY_COMMAND_FILE); } - wipe_misc(); - rename(RECOVERY_COMMAND_FILE_TMP, RECOVERY_COMMAND_FILE); if (do_reboot) reboot_to_recovery(); return 0; } -- cgit v1.2.3 From 381f455cac0905b023dde79625b06c27b6165dd0 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 5 May 2015 18:36:45 -0700 Subject: uncrypt: Switch to C++ Also apply some trivial changes like int -> bool and clean-ups. Change-Id: Ic55fc8b82d7e91b321f69d10175be23d5c04eb92 --- uncrypt/Android.mk | 2 +- uncrypt/uncrypt.c | 467 ---------------------------------------------------- uncrypt/uncrypt.cpp | 465 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 466 insertions(+), 468 deletions(-) delete mode 100644 uncrypt/uncrypt.c create mode 100644 uncrypt/uncrypt.cpp (limited to 'uncrypt') diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index 878d2757e..d832d9724 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -16,7 +16,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES := uncrypt.c +LOCAL_SRC_FILES := uncrypt.cpp LOCAL_MODULE := uncrypt diff --git a/uncrypt/uncrypt.c b/uncrypt/uncrypt.c deleted file mode 100644 index 42ae649cc..000000000 --- a/uncrypt/uncrypt.c +++ /dev/null @@ -1,467 +0,0 @@ -/* - * 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. - */ - -// This program takes a file on an ext4 filesystem and produces a list -// of the blocks that file occupies, which enables the file contents -// to be read directly from the block device without mounting the -// filesystem. -// -// If the filesystem is using an encrypted block device, it will also -// read the file and rewrite it to the same blocks of the underlying -// (unencrypted) block device, so the file contents can be read -// without the need for the decryption key. -// -// The output of this program is a "block map" which looks like this: -// -// /dev/block/platform/msm_sdcc.1/by-name/userdata # block device -// 49652 4096 # file size in bytes, block size -// 3 # count of block ranges -// 1000 1008 # block range 0 -// 2100 2102 # ... block range 1 -// 30 33 # ... block range 2 -// -// Each block range represents a half-open interval; the line "30 33" -// reprents the blocks [30, 31, 32]. -// -// Recovery can take this block map file and retrieve the underlying -// file data to use as an update package. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define LOG_TAG "uncrypt" -#include -#include -#include - -#define WINDOW_SIZE 5 -#define RECOVERY_COMMAND_FILE "/cache/recovery/command" -#define RECOVERY_COMMAND_FILE_TMP "/cache/recovery/command.tmp" -#define CACHE_BLOCK_MAP "/cache/recovery/block.map" - -static struct fstab* fstab = NULL; - -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)); - 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; - } - return 0; -} - -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]) { - // 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]; - } 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 = realloc(*ranges, *range_alloc * 2 * sizeof(int)); - } - - ++*range_used; - (*ranges)[*range_used*2-2] = new_block; - (*ranges)[*range_used*2-1] = new_block+1; - } -} - -static struct fstab* read_fstab() -{ - fstab = NULL; - - // 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"); - return NULL; - } - - fstab = fs_mgr_read_fstab(fstab_path); - if (!fstab) { - ALOGE("failed to read %s\n", fstab_path); - return NULL; - } - - return fstab; -} - -const char* find_block_device(const char* path, int* encryptable, int* encrypted) -{ - // Look for a volume whose mount point is the prefix of path and - // return its block device. Set encrypted if it's currently - // encrypted. - int i; - for (i = 0; i < fstab->num_entries; ++i) { - struct fstab_rec* v = &fstab->recs[i]; - if (!v->mount_point) continue; - int len = strlen(v->mount_point); - if (strncmp(path, v->mount_point, len) == 0 && - (path[len] == '/' || path[len] == 0)) { - *encrypted = 0; - *encryptable = 0; - if (fs_mgr_is_encryptable(v)) { - *encryptable = 1; - char buffer[PROPERTY_VALUE_MAX+1]; - if (property_get("ro.crypto.state", buffer, "") && - strcmp(buffer, "encrypted") == 0) { - *encrypted = 1; - } - } - return v->blk_device; - } - } - - return NULL; -} - -// Parse the command file RECOVERY_COMMAND_FILE to find the update package -// name. If it's on the /data partition, replace the package name with the -// 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 (f == NULL) { - return NULL; - } - 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) { - char* newline = strchr(fn, '\n'); - if (newline) *newline = 0; - } - return fn; -} - -int produce_block_map(const char* path, const char* map_file, const char* blk_dev, - int encrypted) -{ - struct stat sb; - int ret; - - int mapfd = open(map_file, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR); - if (mapfd < 0) { - ALOGE("failed to open %s\n", map_file); - return -1; - } - FILE* mapf = fdopen(mapfd, "w"); - - ret = stat(path, &sb); - if (ret != 0) { - ALOGE("failed to stat %s\n", path); - return -1; - } - - ALOGI(" block size: %ld bytes\n", (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); - - int* ranges; - int range_alloc = 1; - int range_used = 1; - ranges = malloc(range_alloc * 2 * sizeof(int)); - ranges[0] = -1; - ranges[1] = -1; - - fprintf(mapf, "%s\n%lld %lu\n", blk_dev, (long long)sb.st_size, (unsigned long)sb.st_blksize); - - unsigned char* buffers[WINDOW_SIZE]; - int i; - if (encrypted) { - for (i = 0; i < WINDOW_SIZE; ++i) { - buffers[i] = malloc(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)); - return -1; - } - - int 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)); - return -1; - } - } - - while (pos < sb.st_size) { - 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); - return -1; - } - add_block_to_ranges(&ranges, &range_alloc, &range_used, block); - if (encrypted) { - if (write_at_offset(buffers[head], sb.st_blksize, wfd, (off64_t)sb.st_blksize * block) != 0) { - return -1; - } - } - head = (head + 1) % WINDOW_SIZE; - ++head_block; - } - - // 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; - } - } else { - // If we're not encrypting; we don't need to actually read - // anything, just skip pos forward as if we'd read a - // block. - pos += sb.st_blksize; - } - tail = (tail+1) % WINDOW_SIZE; - } - - 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); - return -1; - } - add_block_to_ranges(&ranges, &range_alloc, &range_used, block); - if (encrypted) { - if (write_at_offset(buffers[head], sb.st_blksize, wfd, (off64_t)sb.st_blksize * block) != 0) { - return -1; - } - } - head = (head + 1) % WINDOW_SIZE; - ++head_block; - } - - fprintf(mapf, "%d\n", range_used); - for (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)); - return -1; - } - fclose(mapf); - close(fd); - if (encrypted) { - if (fsync(wfd) == -1) { - ALOGE("failed to fsync \"%s\": %s\n", blk_dev, strerror(errno)); - return -1; - } - close(wfd); - } - - return 0; -} - -void wipe_misc() { - ALOGI("removing old commands from misc"); - int i; - for (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); - } - } -} - -void reboot_to_recovery() { - ALOGI("rebooting to recovery"); - property_set("sys.powerctl", "reboot,recovery"); - sleep(10); - ALOGE("reboot didn't succeed?"); -} - -int main(int argc, char** argv) -{ - const char* input_path; - const char* map_file; - int do_reboot = 1; - - if (argc != 1 && argc != 3) { - fprintf(stderr, "usage: %s [ ]\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 = 0; - } 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 - // absolute path, so we can find what filesystem it's on. - char path[PATH_MAX+1]; - if (realpath(input_path, path) == NULL) { - ALOGE("failed to convert %s to absolute path: %s", input_path, strerror(errno)); - return 1; - } - - int encryptable; - int encrypted; - if (read_fstab() == NULL) { - return 1; - } - const char* blk_dev = find_block_device(path, &encryptable, &encrypted); - if (blk_dev == NULL) { - ALOGE("failed to find block device for %s", path); - return 1; - } - - // 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"); - - // Recovery supports installing packages from 3 paths: /cache, - // /data, and /sdcard. (On a particular device, other locations - // may work, but those are three we actually expect.) - // - // 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 - // and /sdcard we leave the file alone. - 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); - if (produce_block_map(path, map_file, blk_dev, encrypted) != 0) { - return 1; - } - wipe_misc(); - rename(RECOVERY_COMMAND_FILE_TMP, RECOVERY_COMMAND_FILE); - } - - if (do_reboot) reboot_to_recovery(); - return 0; -} diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp new file mode 100644 index 000000000..11766f14c --- /dev/null +++ b/uncrypt/uncrypt.cpp @@ -0,0 +1,465 @@ +/* + * 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. + */ + +// This program takes a file on an ext4 filesystem and produces a list +// of the blocks that file occupies, which enables the file contents +// to be read directly from the block device without mounting the +// filesystem. +// +// If the filesystem is using an encrypted block device, it will also +// read the file and rewrite it to the same blocks of the underlying +// (unencrypted) block device, so the file contents can be read +// without the need for the decryption key. +// +// The output of this program is a "block map" which looks like this: +// +// /dev/block/platform/msm_sdcc.1/by-name/userdata # block device +// 49652 4096 # file size in bytes, block size +// 3 # count of block ranges +// 1000 1008 # block range 0 +// 2100 2102 # ... block range 1 +// 30 33 # ... block range 2 +// +// Each block range represents a half-open interval; the line "30 33" +// reprents the blocks [30, 31, 32]. +// +// Recovery can take this block map file and retrieve the underlying +// file data to use as an update package. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "uncrypt" +#include +#include +#include + +#define WINDOW_SIZE 5 +#define RECOVERY_COMMAND_FILE "/cache/recovery/command" +#define RECOVERY_COMMAND_FILE_TMP "/cache/recovery/command.tmp" +#define CACHE_BLOCK_MAP "/cache/recovery/block.map" + +static struct fstab* fstab = NULL; + +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)); + 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; + } + 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]) { + // 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]; + } 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(realloc(*ranges, *range_alloc * 2 * sizeof(int))); + } + + ++*range_used; + (*ranges)[*range_used*2-2] = new_block; + (*ranges)[*range_used*2-1] = new_block+1; + } +} + +static struct fstab* read_fstab() { + fstab = NULL; + + // 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"); + return NULL; + } + + fstab = fs_mgr_read_fstab(fstab_path); + if (!fstab) { + ALOGE("failed to read %s\n", fstab_path); + return NULL; + } + + return fstab; +} + +static const char* find_block_device(const char* path, bool* encryptable, bool* encrypted) { + // Look for a volume whose mount point is the prefix of path and + // return its block device. Set encrypted if it's currently + // encrypted. + for (int i = 0; i < fstab->num_entries; ++i) { + struct fstab_rec* v = &fstab->recs[i]; + if (!v->mount_point) { + continue; + } + int len = strlen(v->mount_point); + if (strncmp(path, v->mount_point, len) == 0 && + (path[len] == '/' || path[len] == 0)) { + *encrypted = false; + *encryptable = false; + if (fs_mgr_is_encryptable(v)) { + *encryptable = true; + char buffer[PROPERTY_VALUE_MAX+1]; + if (property_get("ro.crypto.state", buffer, "") && + strcmp(buffer, "encrypted") == 0) { + *encrypted = true; + } + } + return v->blk_device; + } + } + + return NULL; +} + +// Parse the command file RECOVERY_COMMAND_FILE to find the update package +// name. If it's on the /data partition, replace the package name with the +// 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 (f == NULL) { + return NULL; + } + 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) { + char* newline = strchr(fn, '\n'); + if (newline) { + *newline = 0; + } + } + return fn; +} + +static int produce_block_map(const char* path, const char* map_file, const char* blk_dev, + bool encrypted) { + + int mapfd = open(map_file, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR); + if (mapfd < 0) { + ALOGE("failed to open %s\n", map_file); + return -1; + } + FILE* mapf = fdopen(mapfd, "w"); + + struct stat sb; + int ret = stat(path, &sb); + if (ret != 0) { + ALOGE("failed to stat %s\n", path); + return -1; + } + + ALOGI(" block size: %ld bytes\n", (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); + + int range_alloc = 1; + int range_used = 1; + int* ranges = reinterpret_cast(malloc(range_alloc * 2 * sizeof(int))); + ranges[0] = -1; + ranges[1] = -1; + + fprintf(mapf, "%s\n%lld %lu\n", blk_dev, (long long)sb.st_size, (unsigned long)sb.st_blksize); + + unsigned char* buffers[WINDOW_SIZE]; + if (encrypted) { + for (size_t i = 0; i < WINDOW_SIZE; ++i) { + buffers[i] = reinterpret_cast(malloc(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)); + return -1; + } + + int 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)); + return -1; + } + } + + while (pos < sb.st_size) { + 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); + return -1; + } + add_block_to_ranges(&ranges, &range_alloc, &range_used, block); + if (encrypted) { + if (write_at_offset(buffers[head], sb.st_blksize, wfd, + (off64_t)sb.st_blksize * block) != 0) { + return -1; + } + } + head = (head + 1) % WINDOW_SIZE; + ++head_block; + } + + // 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; + } + } else { + // If we're not encrypting; we don't need to actually read + // anything, just skip pos forward as if we'd read a + // block. + pos += sb.st_blksize; + } + tail = (tail+1) % WINDOW_SIZE; + } + + 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); + return -1; + } + add_block_to_ranges(&ranges, &range_alloc, &range_used, block); + if (encrypted) { + if (write_at_offset(buffers[head], sb.st_blksize, wfd, + (off64_t)sb.st_blksize * block) != 0) { + return -1; + } + } + head = (head + 1) % WINDOW_SIZE; + ++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)); + return -1; + } + fclose(mapf); + close(fd); + if (encrypted) { + if (fsync(wfd) == -1) { + ALOGE("failed to fsync \"%s\": %s\n", blk_dev, strerror(errno)); + return -1; + } + close(wfd); + } + + return 0; +} + +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); + } + } +} + +static void reboot_to_recovery() { + ALOGI("rebooting to recovery"); + property_set("sys.powerctl", "reboot,recovery"); + sleep(10); + ALOGE("reboot didn't succeed?"); +} + +int main(int argc, char** argv) +{ + const char* input_path; + const char* map_file; + bool do_reboot = true; + + if (argc != 1 && argc != 3) { + fprintf(stderr, "usage: %s [ ]\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 + // absolute path, so we can find what filesystem it's on. + char path[PATH_MAX+1]; + if (realpath(input_path, path) == NULL) { + ALOGE("failed to convert %s to absolute path: %s", input_path, strerror(errno)); + return 1; + } + + if (read_fstab() == NULL) { + return 1; + } + + bool encryptable; + bool encrypted; + const char* blk_dev = find_block_device(path, &encryptable, &encrypted); + if (blk_dev == NULL) { + ALOGE("failed to find block device for %s", path); + return 1; + } + + // 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"); + + // Recovery supports installing packages from 3 paths: /cache, + // /data, and /sdcard. (On a particular device, other locations + // may work, but those are three we actually expect.) + // + // 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 + // and /sdcard we leave the file alone. + 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); + if (produce_block_map(path, map_file, blk_dev, encrypted) != 0) { + return 1; + } + wipe_misc(); + rename(RECOVERY_COMMAND_FILE_TMP, RECOVERY_COMMAND_FILE); + } + + if (do_reboot) { + reboot_to_recovery(); + } + return 0; +} -- cgit v1.2.3 From 752386319c0d9fb7e4e429a0644086b318d3b4b5 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 27 May 2015 14:46:17 -0700 Subject: Clean up the sleep()'s after poking init services Change-Id: I77564fe5c59e604f1377b278681b7d1bff53a77a --- uncrypt/uncrypt.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'uncrypt') diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 11766f14c..d71271d8e 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -40,18 +40,20 @@ // file data to use as an update package. #include +#include +#include +#include #include #include #include -#include #include #include -#include -#include #include +#include #define LOG_TAG "uncrypt" #include +#include #include #include @@ -376,7 +378,9 @@ static void wipe_misc() { static void reboot_to_recovery() { ALOGI("rebooting to recovery"); property_set("sys.powerctl", "reboot,recovery"); - sleep(10); + while (true) { + pause(); + } ALOGE("reboot didn't succeed?"); } -- cgit v1.2.3 From 80e46e08de5f65702fa7f7cd3ef83f905d919bbc Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 3 Jun 2015 10:49:29 -0700 Subject: recovery: Switch to clang And a few trival fixes to suppress warnings. Change-Id: I38734b5f4434643e85feab25f4807b46a45d8d65 --- uncrypt/Android.mk | 2 ++ 1 file changed, 2 insertions(+) (limited to 'uncrypt') diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index d832d9724..6859e75e4 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -16,6 +16,8 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) +LOCAL_CLANG := true + LOCAL_SRC_FILES := uncrypt.cpp LOCAL_MODULE := uncrypt -- cgit v1.2.3 From 383b00d0e498e1f3b84e9dcfc6dddec6a76379d7 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 21 May 2015 16:44:44 -0700 Subject: 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 (cherry picked from commit 158e11d6738a751b754d09df7275add589c31191) Change-Id: I349f6d368a0d6f6ee4332831c4cd4075a47426ff --- uncrypt/Android.mk | 2 +- uncrypt/uncrypt.cpp | 185 ++++++++++++++++++++++++++-------------------------- 2 files changed, 92 insertions(+), 95 deletions(-) (limited to 'uncrypt') diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index 6859e75e4..e73c8f1b6 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -22,6 +22,6 @@ LOCAL_SRC_FILES := uncrypt.cpp LOCAL_MODULE := uncrypt -LOCAL_STATIC_LIBRARIES := libfs_mgr liblog libcutils +LOCAL_STATIC_LIBRARIES := libbase liblog libfs_mgr libcutils include $(BUILD_EXECUTABLE) diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index d71271d8e..efdbdac3c 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -46,21 +46,25 @@ #include #include #include -#include -#include #include +#include +#include #include -#define LOG_TAG "uncrypt" -#include +#include +#include #include #include #include +#define LOG_TAG "uncrypt" +#include + #define WINDOW_SIZE 5 -#define RECOVERY_COMMAND_FILE "/cache/recovery/command" -#define RECOVERY_COMMAND_FILE_TMP "/cache/recovery/command.tmp" -#define CACHE_BLOCK_MAP "/cache/recovery/block.map" + +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"; static struct fstab* fstab = NULL; @@ -157,65 +161,35 @@ static const char* find_block_device(const char* path, bool* encryptable, bool* return NULL; } -// Parse the command file RECOVERY_COMMAND_FILE to find the update package -// name. If it's on the /data partition, replace the package name with the -// 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() +// Parse uncrypt_file to find the update package name. +static bool find_uncrypt_package(std::string& package_name) { - FILE* f = fopen(RECOVERY_COMMAND_FILE, "r"); - if (f == NULL) { - return NULL; - } - 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); + if (!android::base::ReadFileToString(uncrypt_file, &package_name)) { + ALOGE("failed to open \"%s\": %s\n", uncrypt_file.c_str(), strerror(errno)); + return false; } - 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) { - char* newline = strchr(fn, '\n'); - if (newline) { - *newline = 0; - } - } - return fn; + // Remove the trailing '\n' if present. + package_name = android::base::Trim(package_name); + + return true; } 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); - if (mapfd < 0) { + if (mapfd == -1) { ALOGE("failed to open %s\n", map_file); 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()); + return -1; + } + struct stat sb; int ret = stat(path, &sb); if (ret != 0) { @@ -261,7 +235,15 @@ static int produce_block_map(const char* path, const char* map_file, const char* } } + int last_progress = 0; while (pos < sb.st_size) { + // Update the status file, progress must be between [0, 99]. + int progress = static_cast(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) { // write out head buffer int block = head_block; @@ -384,43 +366,15 @@ static void reboot_to_recovery() { ALOGE("reboot didn't succeed?"); } -int main(int argc, char** argv) -{ - const char* input_path; - const char* map_file; - bool do_reboot = true; +int uncrypt(const char* input_path, const char* map_file, int status_fd) { - if (argc != 1 && argc != 3) { - fprintf(stderr, "usage: %s [ ]\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); + ALOGI("update package is \"%s\"", input_path); // Turn the name of the file we're supposed to convert into an // absolute path, so we can find what filesystem it's on. char path[PATH_MAX+1]; 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; } @@ -449,21 +403,64 @@ int main(int argc, char** argv) // 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 // and /sdcard we leave the file alone. - if (strncmp(path, "/data/", 6) != 0) { - // path does not start with "/data/"; leave it alone. - unlink(RECOVERY_COMMAND_FILE_TMP); - wipe_misc(); - } else { + if (strncmp(path, "/data/", 6) == 0) { 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; } - wipe_misc(); - rename(RECOVERY_COMMAND_FILE_TMP, RECOVERY_COMMAND_FILE); } - if (do_reboot) { + 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] [ ]\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; + } + wipe_misc(); 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(); + } + + // 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; + } + 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; } -- cgit v1.2.3 From ac6aa7ede0b0b1df18b4149fdb9846c3e486918a Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 29 May 2015 14:24:02 -0700 Subject: uncrypt: Write status when it reboots to factory reset When it reboots into recovery for a factory reset, it still needs to write the uncrypt status (-1) to the pipe. Bug: 21511893 (cherry picked from commit 2c2cae8a4a18b85043bb6260a59ac7d1589016bf) Change-Id: Ia5a75c5edf3afbd916153da1b4de4db2f00d0209 --- uncrypt/uncrypt.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'uncrypt') diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index efdbdac3c..20a272949 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -431,26 +431,29 @@ int main(int argc, char** argv) { wipe_misc(); reboot_to_recovery(); } else { - std::string package; + // 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; + } + 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(); } - // 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; - } int status = uncrypt(input_path, map_file, status_fd); if (status != 0) { android::base::WriteStringToFd("-1\n", status_fd); -- cgit v1.2.3 From 7cf50c60b5c955010a8b8d0c23264f03ab673deb Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 16 Jul 2015 20:04:13 -0700 Subject: uncrypt: Support file level encryption. Bug: 22534003 Change-Id: I2bc22418c416491da573875dce78daed24f2c046 (cherry picked from commit 6e9dda70cb00dd1f1948e071d7df7ca6e2bd8332) --- uncrypt/uncrypt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'uncrypt') diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 20a272949..8785b29af 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -146,7 +146,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, "") && -- cgit v1.2.3 From c754792a07d340c30128b2fd064a58b1f15623da Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 6 Aug 2015 18:35:05 -0700 Subject: Use unique_ptr and unique_fd to manager FDs. Clean up leaky file descriptors in uncrypt/uncrypt.cpp. Add unique_fd for open() and unique_file for fopen() to close FDs on destruction. Bug: 21496020 Change-Id: I0174db0de9d5f59cd43b44757b8ef0f5912c91a2 --- uncrypt/Android.mk | 2 ++ uncrypt/uncrypt.cpp | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) (limited to 'uncrypt') diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index e73c8f1b6..f31db4243 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -20,6 +20,8 @@ LOCAL_CLANG := true LOCAL_SRC_FILES := uncrypt.cpp +LOCAL_C_INCLUDES := $(LOCAL_PATH)/.. + LOCAL_MODULE := uncrypt LOCAL_STATIC_LIBRARIES := libbase liblog libfs_mgr libcutils diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 8785b29af..aef480035 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -51,6 +51,8 @@ #include #include +#include + #include #include #include @@ -60,6 +62,8 @@ #define LOG_TAG "uncrypt" #include +#include "unique_fd.h" + #define WINDOW_SIZE 5 static const std::string cache_block_map = "/cache/recovery/block.map"; @@ -183,6 +187,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* return -1; } FILE* mapf = fdopen(mapfd, "w"); + unique_file mapf_holder(mapf); // Make sure we can write to the status_file. if (!android::base::WriteStringToFd("0\n", status_fd)) { @@ -191,8 +196,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* } struct stat sb; - int ret = stat(path, &sb); - if (ret != 0) { + if (stat(path, &sb) != 0) { ALOGE("failed to stat %s\n", path); return -1; } @@ -221,15 +225,18 @@ static int produce_block_map(const char* path, const char* map_file, const char* size_t pos = 0; int fd = open(path, O_RDONLY); - if (fd < 0) { + unique_fd fd_holder(fd); + if (fd == -1) { ALOGE("failed to open fd for reading: %s\n", strerror(errno)); return -1; } int wfd = -1; + unique_fd wfd_holder(wfd); if (encrypted) { wfd = open(blk_dev, O_WRONLY | O_SYNC); - if (wfd < 0) { + wfd_holder = unique_fd(wfd); + if (wfd == -1) { ALOGE("failed to open fd for writing: %s\n", strerror(errno)); return -1; } @@ -247,8 +254,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* if ((tail+1) % WINDOW_SIZE == head) { // write out head buffer int block = head_block; - ret = ioctl(fd, FIBMAP, &block); - if (ret != 0) { + if (ioctl(fd, FIBMAP, &block) != 0) { ALOGE("failed to find block %d\n", head_block); return -1; } @@ -288,8 +294,7 @@ 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) { + if (ioctl(fd, FIBMAP, &block) != 0) { ALOGE("failed to find block %d\n", head_block); return -1; } @@ -313,14 +318,11 @@ static int produce_block_map(const char* path, const char* map_file, const char* ALOGE("failed to fsync \"%s\": %s\n", map_file, 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)); return -1; } - close(wfd); } return 0; @@ -333,6 +335,8 @@ static void wipe_misc() { if (!v->mount_point) continue; if (strcmp(v->mount_point, "/misc") == 0) { int fd = open(v->blk_device, O_WRONLY | O_SYNC); + unique_fd fd_holder(fd); + uint8_t zeroes[1088]; // sizeof(bootloader_message) from recovery memset(zeroes, 0, sizeof(zeroes)); @@ -349,10 +353,8 @@ static void wipe_misc() { } if (fsync(fd) == -1) { ALOGE("failed to fsync \"%s\": %s\n", v->blk_device, strerror(errno)); - close(fd); return; } - close(fd); } } } @@ -437,6 +439,7 @@ int main(int argc, char** argv) { ALOGE("failed to open pipe \"%s\": %s\n", status_file.c_str(), strerror(errno)); return 1; } + unique_fd status_fd_holder(status_fd); if (argc == 3) { // when command-line args are given this binary is being used @@ -447,7 +450,6 @@ int main(int argc, char** argv) { 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(); @@ -457,12 +459,10 @@ int main(int argc, char** argv) { 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; -- cgit v1.2.3 From daa6d04434cdd104a39909aca38e96743689c92f Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Thu, 3 Sep 2015 16:32:39 -0700 Subject: move uncrypt from init.rc to uncrypt.rc Move uncrypt from /init.rc to /system/etc/init/uncrypt.rc using the LOCAL_INIT_RC mechanism Bug 23186545 Change-Id: Ib8cb6dffd2212f524298279787fd557bc84aa7b9 --- uncrypt/Android.mk | 2 ++ uncrypt/uncrypt.rc | 9 +++++++++ 2 files changed, 11 insertions(+) create mode 100644 uncrypt/uncrypt.rc (limited to 'uncrypt') diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index f31db4243..6422cb2f4 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -26,4 +26,6 @@ LOCAL_MODULE := uncrypt LOCAL_STATIC_LIBRARIES := libbase liblog libfs_mgr libcutils +LOCAL_INIT_RC := uncrypt.rc + include $(BUILD_EXECUTABLE) diff --git a/uncrypt/uncrypt.rc b/uncrypt/uncrypt.rc new file mode 100644 index 000000000..5f4c47936 --- /dev/null +++ b/uncrypt/uncrypt.rc @@ -0,0 +1,9 @@ +service uncrypt /system/bin/uncrypt + class main + disabled + oneshot + +service pre-recovery /system/bin/uncrypt --reboot + class main + disabled + oneshot -- cgit v1.2.3 From cc4e3c6002efc42bce314c98909ecfc2d2f2ab02 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Wed, 4 Nov 2015 11:43:58 -0800 Subject: uncrypt: remove O_SYNC to avoid time-out failures This patch removes costly O_SYNC flag for encrypted block device. After writing whole decrypted blocks, fsync should guarantee their consistency from further power failures. This patch reduces the elapsed time significantly consumed by upgrading packages on an encrypted partition, so that it could avoid another time-out failures too. Change-Id: I1fb9022c83ecc00bad09d107fc87a6a09babb0ec Signed-off-by: Jaegeuk Kim --- uncrypt/uncrypt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'uncrypt') diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index aef480035..6db438258 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -234,7 +234,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* int wfd = -1; unique_fd wfd_holder(wfd); if (encrypted) { - wfd = open(blk_dev, O_WRONLY | O_SYNC); + wfd = open(blk_dev, O_WRONLY); wfd_holder = unique_fd(wfd); if (wfd == -1) { ALOGE("failed to open fd for writing: %s\n", strerror(errno)); -- cgit v1.2.3 From 63b089e3aa9302206fbfa8260804e501e6483b83 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Thu, 12 Nov 2015 21:07:55 -0800 Subject: We can use fclose directly in std::unique_ptr. It turns out the standard explicitly states that if the pointer is null, the deleter function won't be called. So it doesn't matter that fclose(3) doesn't accept null. Change-Id: I10e6e0d62209ec03ac60e673edd46f32ba279a04 --- uncrypt/uncrypt.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'uncrypt') diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 6db438258..4956cc297 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -186,8 +186,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* ALOGE("failed to open %s\n", map_file); return -1; } - FILE* mapf = fdopen(mapfd, "w"); - unique_file mapf_holder(mapf); + std::unique_ptr mapf(fdopen(mapfd, "w"), fclose); // Make sure we can write to the status_file. if (!android::base::WriteStringToFd("0\n", status_fd)) { @@ -212,7 +211,8 @@ static int produce_block_map(const char* path, const char* map_file, const char* ranges[0] = -1; ranges[1] = -1; - fprintf(mapf, "%s\n%lld %lu\n", blk_dev, (long long)sb.st_size, (unsigned long)sb.st_blksize); + fprintf(mapf.get(), "%s\n%lld %lu\n", + blk_dev, (long long)sb.st_size, (unsigned long)sb.st_blksize); unsigned char* buffers[WINDOW_SIZE]; if (encrypted) { @@ -309,9 +309,9 @@ static int produce_block_map(const char* path, const char* map_file, const char* ++head_block; } - fprintf(mapf, "%d\n", range_used); + fprintf(mapf.get(), "%d\n", range_used); for (int i = 0; i < range_used; ++i) { - fprintf(mapf, "%d %d\n", ranges[i*2], ranges[i*2+1]); + fprintf(mapf.get(), "%d %d\n", ranges[i*2], ranges[i*2+1]); } if (fsync(mapfd) == -1) { -- cgit v1.2.3 From 4b166f0e69d46858ff998414da2a01e0266fa339 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Fri, 4 Dec 2015 15:30:20 -0800 Subject: Track rename from base/ to android-base/. Change-Id: I354a8c424d340a9abe21fd716a4ee0d3b177d86f --- uncrypt/uncrypt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'uncrypt') diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 4956cc297..482504192 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -53,8 +53,8 @@ #include -#include -#include +#include +#include #include #include #include -- cgit v1.2.3 From b8df5fb90e4b7244fa7925b9706cdd218e18a2aa Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 8 Dec 2015 22:47:25 -0800 Subject: uncrypt: Suppress the compiler warnings on LP64. We have the following warnings when compiling uncrypt on LP64 (e.g. aosp_angler-userdebug). bootable/recovery/uncrypt/uncrypt.cpp:77:53: warning: format specifies type 'long long' but the argument has type 'off64_t' (aka 'long') [-Wformat] ALOGE("error seeking to offset %lld: %s\n", offset, strerror(errno)); ~~~~ ^~~~~~ %ld bootable/recovery/uncrypt/uncrypt.cpp:84:54: warning: format specifies type 'long long' but the argument has type 'unsigned long' [-Wformat] ALOGE("error writing offset %lld: %s\n", (offset + written), strerror(errno)); ~~~~ ^~~~~~~~~~~~~~~~~~ %lu bootable/recovery/uncrypt/uncrypt.cpp:246:16: warning: comparison of integers of different signs: 'size_t' (aka 'unsigned long') and 'off_t' (aka 'long') [-Wsign-compare] while (pos < sb.st_size) { ~~~ ^ ~~~~~~~~~~ According to POSIX spec [1], we have: off_t and blksize_t shall be signed integer types; size_t shall be an unsigned integer type; blksize_t and size_t are no greater than the width of type long. And on Android, we always have a 64-bit st_size from stat(2) (//bionic/libc/include/sys/stat.h). Fix the type and add necessary casts to suppress the warnings. [1] http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html Change-Id: I5d64d5b7919c541441176c364752de047f9ecb20 --- uncrypt/uncrypt.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'uncrypt') diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 482504192..20efbe4df 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -41,6 +41,7 @@ #include #include +#include #include #include #include @@ -74,14 +75,15 @@ static struct fstab* fstab = NULL; 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\n", 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)); + ALOGE("error writing offset %" PRId64 ": %s\n", + offset + static_cast(written), strerror(errno)); return -1; } written += wrote; @@ -200,10 +202,10 @@ static int produce_block_map(const char* path, const char* map_file, const char* return -1; } - ALOGI(" block size: %ld bytes\n", (long)sb.st_blksize); + ALOGI(" block size: %ld bytes\n", static_cast(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\n", sb.st_size, blocks); int range_alloc = 1; int range_used = 1; @@ -211,8 +213,8 @@ static int produce_block_map(const char* path, const char* map_file, const char* ranges[0] = -1; ranges[1] = -1; - fprintf(mapf.get(), "%s\n%lld %lu\n", - blk_dev, (long long)sb.st_size, (unsigned long)sb.st_blksize); + fprintf(mapf.get(), "%s\n%" PRId64 " %ld\n", + blk_dev, sb.st_size, static_cast(sb.st_blksize)); unsigned char* buffers[WINDOW_SIZE]; if (encrypted) { @@ -222,7 +224,6 @@ static int produce_block_map(const char* path, const char* map_file, const char* } int head_block = 0; int head = 0, tail = 0; - size_t pos = 0; int fd = open(path, O_RDONLY); unique_fd fd_holder(fd); @@ -242,6 +243,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* } } + off64_t pos = 0; int last_progress = 0; while (pos < sb.st_size) { // Update the status file, progress must be between [0, 99]. @@ -261,7 +263,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* add_block_to_ranges(&ranges, &range_alloc, &range_used, block); if (encrypted) { if (write_at_offset(buffers[head], sb.st_blksize, wfd, - (off64_t)sb.st_blksize * block) != 0) { + static_cast(sb.st_blksize) * block) != 0) { return -1; } } @@ -272,7 +274,7 @@ 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) { + while (so_far < static_cast(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) { @@ -301,7 +303,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* add_block_to_ranges(&ranges, &range_alloc, &range_used, block); if (encrypted) { if (write_at_offset(buffers[head], sb.st_blksize, wfd, - (off64_t)sb.st_blksize * block) != 0) { + static_cast(sb.st_blksize) * block) != 0) { return -1; } } -- cgit v1.2.3 From c5631fc09666a9542d2882299d40500d18d1f68c Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Tue, 12 Jan 2016 16:54:44 -0500 Subject: uncrypt: avoid use-after-free The `std::string package` variable goes out of scope but the input_path variable is then used to access the memory as it's set to `c_str()`. This was detected via OpenBSD malloc's junk filling feature. Change-Id: Ic4b939347881b6ebebf71884e7e2272ce99510e2 --- uncrypt/uncrypt.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'uncrypt') diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 20efbe4df..de7e48182 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -418,8 +418,6 @@ int uncrypt(const char* input_path, const char* map_file, int status_fd) { } 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] [ ]\n", argv[0]); @@ -443,13 +441,16 @@ int main(int argc, char** argv) { } unique_fd status_fd_holder(status_fd); + std::string package; + const char* input_path; + const char* map_file; + 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); return 1; -- cgit v1.2.3 From 25dd0386fe69460cd1d39de116197dd2c7bf9ec2 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Mon, 1 Feb 2016 11:40:37 -0800 Subject: uncrypt: generate map file by renaming tmp file. Writing map file directly can break consistency in map file if it fails in the middle. Instead, we write a temporary file and rename the temporary file to map file. Bug: 26883096 Change-Id: I5e99e942e1b75e758af5f7a48f8a08a0b0041d6a --- uncrypt/uncrypt.cpp | 168 +++++++++++++++++++++++++++++----------------------- 1 file changed, 93 insertions(+), 75 deletions(-) (limited to 'uncrypt') diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index de7e48182..098a7a979 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -52,9 +53,12 @@ #include #include +#include #include +#include #include +#include #include #include #include @@ -78,44 +82,22 @@ static int write_at_offset(unsigned char* buffer, size_t size, int wfd, off64_t ALOGE("error seeking to offset %" PRId64 ": %s\n", 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 %" PRId64 ": %s\n", - offset + static_cast(written), strerror(errno)); - return -1; - } - written += wrote; + if (!android::base::WriteFully(wfd, buffer, size)) { + ALOGE("error writing offset %" PRId64 ": %s\n", 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& 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(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); } } @@ -183,12 +165,17 @@ static bool find_uncrypt_package(std::string& package_name) 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); + std::string err; + if (!android::base::RemoveFileIfExists(map_file, &err)) { + ALOGE("failed to remove the existing map file %s: %s\n", 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; } - std::unique_ptr mapf(fdopen(mapfd, "w"), fclose); // Make sure we can write to the status_file. if (!android::base::WriteStringToFd("0\n", status_fd)) { @@ -207,37 +194,32 @@ static int produce_block_map(const char* path, const char* map_file, const char* int blocks = ((sb.st_size-1) / sb.st_blksize) + 1; ALOGI(" file size: %" PRId64 " bytes, %d blocks\n", sb.st_size, blocks); - int range_alloc = 1; - int range_used = 1; - int* ranges = reinterpret_cast(malloc(range_alloc * 2 * sizeof(int))); - ranges[0] = -1; - ranges[1] = -1; + std::vector ranges; - fprintf(mapf.get(), "%s\n%" PRId64 " %ld\n", - blk_dev, sb.st_size, static_cast(sb.st_blksize)); + std::string s = android::base::StringPrintf("%s\n%" PRId64 " %ld\n", + blk_dev, sb.st_size, static_cast(sb.st_blksize)); + if (!android::base::WriteStringToFd(s, mapfd.get())) { + ALOGE("failed to write %s: %s\n", tmp_map_file.c_str(), strerror(errno)); + return -1; + } - unsigned char* buffers[WINDOW_SIZE]; + std::vector> buffers; if (encrypted) { - for (size_t i = 0; i < WINDOW_SIZE; ++i) { - buffers[i] = reinterpret_cast(malloc(sb.st_blksize)); - } + buffers.resize(WINDOW_SIZE, std::vector(sb.st_blksize)); } int head_block = 0; int head = 0, tail = 0; - int fd = open(path, O_RDONLY); - unique_fd fd_holder(fd); - if (fd == -1) { - 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\n", path, strerror(errno)); return -1; } - int wfd = -1; - unique_fd wfd_holder(wfd); + unique_fd wfd(-1); if (encrypted) { wfd = open(blk_dev, O_WRONLY); - wfd_holder = unique_fd(wfd); - if (wfd == -1) { + if (!wfd) { ALOGE("failed to open fd for writing: %s\n", strerror(errno)); return -1; } @@ -256,13 +238,13 @@ static int produce_block_map(const char* path, const char* map_file, const char* if ((tail+1) % WINDOW_SIZE == head) { // write out head buffer int block = head_block; - if (ioctl(fd, FIBMAP, &block) != 0) { + if (ioctl(fd.get(), FIBMAP, &block) != 0) { ALOGE("failed to find block %d\n", 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, + if (write_at_offset(buffers[head].data(), sb.st_blksize, wfd.get(), static_cast(sb.st_blksize) * block) != 0) { return -1; } @@ -273,17 +255,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 < static_cast(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( + std::min(static_cast(sb.st_blksize), sb.st_size - pos)); + if (!android::base::ReadFully(fd.get(), buffers[tail].data(), to_read)) { + ALOGE("failed to read: %s\n", 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 @@ -296,13 +274,13 @@ 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; - if (ioctl(fd, FIBMAP, &block) != 0) { + if (ioctl(fd.get(), FIBMAP, &block) != 0) { ALOGE("failed to find block %d\n", 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, + if (write_at_offset(buffers[head].data(), sb.st_blksize, wfd.get(), static_cast(sb.st_blksize) * block) != 0) { return -1; } @@ -311,22 +289,62 @@ static int produce_block_map(const char* path, const char* map_file, const char* ++head_block; } - fprintf(mapf.get(), "%d\n", range_used); - for (int i = 0; i < range_used; ++i) { - fprintf(mapf.get(), "%d %d\n", ranges[i*2], ranges[i*2+1]); + if (!android::base::WriteStringToFd( + android::base::StringPrintf("%zu\n", ranges.size() / 2), mapfd.get())) { + ALOGE("failed to write %s: %s\n", tmp_map_file.c_str(), strerror(errno)); + return -1; + } + 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\n", tmp_map_file.c_str(), strerror(errno)); + return -1; + } } - if (fsync(mapfd) == -1) { - ALOGE("failed to fsync \"%s\": %s\n", map_file, strerror(errno)); + if (fsync(mapfd.get()) == -1) { + ALOGE("failed to fsync \"%s\": %s\n", tmp_map_file.c_str(), strerror(errno)); + return -1; + } + if (close(mapfd.get() == -1)) { + ALOGE("failed to close %s: %s\n", tmp_map_file.c_str(), strerror(errno)); return -1; } + mapfd = -1; + if (encrypted) { - if (fsync(wfd) == -1) { + if (fsync(wfd.get()) == -1) { ALOGE("failed to fsync \"%s\": %s\n", blk_dev, strerror(errno)); return -1; } + if (close(wfd.get()) == -1) { + ALOGE("failed to close %s: %s\n", blk_dev, strerror(errno)); + return -1; + } + wfd = -1; } + if (rename(tmp_map_file.c_str(), map_file) == -1) { + ALOGE("failed to rename %s to %s: %s\n", 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\n", dir_name.c_str(), strerror(errno)); + return -1; + } + if (fsync(dfd.get()) == -1) { + ALOGE("failed to fsync %s: %s\n", dir_name.c_str(), strerror(errno)); + return -1; + } + if (close(dfd.get() == -1)) { + ALOGE("failed to close %s: %s\n", dir_name.c_str(), strerror(errno)); + return -1; + } + dfd = -1; return 0; } -- cgit v1.2.3 From 2d46da57e19612b0a29ee0b8601146cf8fb9ff5e Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Tue, 26 Jan 2016 16:50:15 -0800 Subject: uncrypt: add options to setup bcb and clear bcb. Bug: 26696173 Change-Id: I3a612f045aaa9e93e61ae45b05300d02b19bb3ad --- uncrypt/uncrypt.cpp | 292 +++++++++++++++++++++++++++++++++------------------- uncrypt/uncrypt.rc | 10 ++ 2 files changed, 198 insertions(+), 104 deletions(-) (limited to 'uncrypt') diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 098a7a979..705744eb6 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -58,6 +58,7 @@ #include #include +#include #include #include #include @@ -67,23 +68,25 @@ #define LOG_TAG "uncrypt" #include +#include "bootloader.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"; +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 struct fstab* fstab = NULL; 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 %" PRId64 ": %s\n", offset, strerror(errno)); + ALOGE("error seeking to offset %" PRId64 ": %s", offset, strerror(errno)); return -1; } if (!android::base::WriteFully(wfd, buffer, size)) { - ALOGE("error writing offset %" PRId64 ": %s\n", offset, strerror(errno)); + ALOGE("error writing offset %" PRId64 ": %s", offset, strerror(errno)); return -1; } return 0; @@ -107,13 +110,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; } @@ -150,16 +153,16 @@ static const char* find_block_device(const char* path, bool* encryptable, bool* } // 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; } @@ -167,7 +170,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* bool encrypted, int status_fd) { std::string err; if (!android::base::RemoveFileIfExists(map_file, &err)) { - ALOGE("failed to remove the existing map file %s: %s\n", map_file, err.c_str()); + 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"; @@ -179,27 +182,27 @@ static int produce_block_map(const char* path, const char* map_file, const char* // 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()); + ALOGE("failed to update \"%s\"\n", STATUS_FILE.c_str()); return -1; } struct stat sb; if (stat(path, &sb) != 0) { - ALOGE("failed to stat %s\n", path); + ALOGE("failed to stat %s", path); return -1; } - ALOGI(" block size: %ld bytes\n", static_cast(sb.st_blksize)); + ALOGI(" block size: %ld bytes", static_cast(sb.st_blksize)); int blocks = ((sb.st_size-1) / sb.st_blksize) + 1; - ALOGI(" file size: %" PRId64 " bytes, %d blocks\n", sb.st_size, blocks); + ALOGI(" file size: %" PRId64 " bytes, %d blocks", sb.st_size, blocks); std::vector ranges; std::string s = android::base::StringPrintf("%s\n%" PRId64 " %ld\n", blk_dev, sb.st_size, static_cast(sb.st_blksize)); if (!android::base::WriteStringToFd(s, mapfd.get())) { - ALOGE("failed to write %s: %s\n", tmp_map_file.c_str(), strerror(errno)); + ALOGE("failed to write %s: %s", tmp_map_file.c_str(), strerror(errno)); return -1; } @@ -212,7 +215,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* unique_fd fd(open(path, O_RDONLY)); if (!fd) { - ALOGE("failed to open %s for reading: %s\n", path, strerror(errno)); + ALOGE("failed to open %s for reading: %s", path, strerror(errno)); return -1; } @@ -220,7 +223,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* if (encrypted) { wfd = open(blk_dev, O_WRONLY); if (!wfd) { - ALOGE("failed to open fd for writing: %s\n", strerror(errno)); + ALOGE("failed to open fd for writing: %s", strerror(errno)); return -1; } } @@ -239,7 +242,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* // write out head buffer int block = head_block; if (ioctl(fd.get(), FIBMAP, &block) != 0) { - ALOGE("failed to find block %d\n", head_block); + ALOGE("failed to find block %d", head_block); return -1; } add_block_to_ranges(ranges, block); @@ -258,7 +261,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* size_t to_read = static_cast( std::min(static_cast(sb.st_blksize), sb.st_size - pos)); if (!android::base::ReadFully(fd.get(), buffers[tail].data(), to_read)) { - ALOGE("failed to read: %s\n", strerror(errno)); + ALOGE("failed to read: %s", strerror(errno)); return -1; } pos += to_read; @@ -275,7 +278,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* // write out head buffer int block = head_block; if (ioctl(fd.get(), FIBMAP, &block) != 0) { - ALOGE("failed to find block %d\n", head_block); + ALOGE("failed to find block %d", head_block); return -1; } add_block_to_ranges(ranges, block); @@ -291,41 +294,41 @@ static int produce_block_map(const char* path, const char* map_file, const char* if (!android::base::WriteStringToFd( android::base::StringPrintf("%zu\n", ranges.size() / 2), mapfd.get())) { - ALOGE("failed to write %s: %s\n", tmp_map_file.c_str(), strerror(errno)); + ALOGE("failed to write %s: %s", tmp_map_file.c_str(), strerror(errno)); return -1; } 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\n", tmp_map_file.c_str(), strerror(errno)); + ALOGE("failed to write %s: %s", tmp_map_file.c_str(), strerror(errno)); return -1; } } if (fsync(mapfd.get()) == -1) { - ALOGE("failed to fsync \"%s\": %s\n", tmp_map_file.c_str(), strerror(errno)); + 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\n", tmp_map_file.c_str(), strerror(errno)); + ALOGE("failed to close %s: %s", tmp_map_file.c_str(), strerror(errno)); return -1; } mapfd = -1; if (encrypted) { if (fsync(wfd.get()) == -1) { - ALOGE("failed to fsync \"%s\": %s\n", blk_dev, strerror(errno)); + ALOGE("failed to fsync \"%s\": %s", blk_dev, strerror(errno)); return -1; } if (close(wfd.get()) == -1) { - ALOGE("failed to close %s: %s\n", blk_dev, strerror(errno)); + ALOGE("failed to close %s: %s", blk_dev, strerror(errno)); return -1; } wfd = -1; } if (rename(tmp_map_file.c_str(), map_file) == -1) { - ALOGE("failed to rename %s to %s: %s\n", tmp_map_file.c_str(), map_file, strerror(errno)); + 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. @@ -333,50 +336,74 @@ static int produce_block_map(const char* path, const char* map_file, const char* 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\n", dir_name.c_str(), strerror(errno)); + 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\n", dir_name.c_str(), strerror(errno)); + 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\n", dir_name.c_str(), strerror(errno)); + ALOGE("failed to close %s: %s", dir_name.c_str(), strerror(errno)); return -1; } dfd = -1; return 0; } -static void wipe_misc() { - ALOGI("removing old commands from misc"); +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) { - 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); - unique_fd fd_holder(fd); - - 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)); - return; - } + fstab_rec* v = &fstab->recs[i]; + if (v->mount_point != nullptr && strcmp(v->mount_point, "/misc") == 0) { + return v->blk_device; } } + return ""; +} + +static int read_bootloader_message(bootloader_message* out) { + std::string misc_blk_device = get_misc_blk_device(); + if (misc_blk_device.empty()) { + ALOGE("failed to find /misc partition."); + return -1; + } + unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY)); + if (!fd) { + ALOGE("failed to open %s: %s", misc_blk_device.c_str(), strerror(errno)); + return -1; + } + if (!android::base::ReadFully(fd.get(), out, sizeof(*out))) { + ALOGE("failed to read %s: %s", misc_blk_device.c_str(), strerror(errno)); + return -1; + } + return 0; +} + +static int write_bootloader_message(const bootloader_message* in) { + std::string misc_blk_device = get_misc_blk_device(); + if (misc_blk_device.empty()) { + ALOGE("failed to find /misc partition."); + return -1; + } + unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY | O_SYNC)); + if (!fd) { + ALOGE("failed to open %s: %s", misc_blk_device.c_str(), strerror(errno)); + return -1; + } + if (!android::base::WriteFully(fd.get(), in, sizeof(*in))) { + ALOGE("failed to write %s: %s", misc_blk_device.c_str(), strerror(errno)); + return -1; + } + // TODO: O_SYNC and fsync() duplicates each other? + if (fsync(fd.get()) == -1) { + ALOGE("failed to fsync %s: %s", misc_blk_device.c_str(), strerror(errno)); + return -1; + } + return 0; } static void reboot_to_recovery() { @@ -388,7 +415,7 @@ static void reboot_to_recovery() { ALOGE("reboot didn't succeed?"); } -int uncrypt(const char* input_path, const char* map_file, int status_fd) { +static int uncrypt(const char* input_path, const char* map_file, int status_fd) { ALOGI("update package is \"%s\"", input_path); @@ -415,8 +442,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 @@ -435,56 +462,113 @@ int uncrypt(const char* input_path, const char* map_file, int status_fd) { return 0; } -int main(int argc, char** argv) { - - if (argc != 3 && argc != 1 && (argc == 2 && strcmp(argv[1], "--reboot") != 0)) { - fprintf(stderr, "usage: %s [--reboot] [ ]\n", argv[0]); - return 2; +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; } - // 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) { + 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; } - wipe_misc(); - reboot_to_recovery(); - } 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; - } - unique_fd status_fd_holder(status_fd); + input_path = package.c_str(); + } + CHECK(map_file != nullptr); + int status = uncrypt(input_path, map_file, status_fd.get()); + if (status != 0) { + android::base::WriteStringToFd("-1\n", status_fd.get()); + return 1; + } + android::base::WriteStringToFd("100\n", status_fd.get()); + return 0; +} - std::string package; - const char* input_path; - const char* map_file; +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; + } + bootloader_message boot = {}; + if (write_bootloader_message(&boot) != 0) { + android::base::WriteStringToFd("-1\n", status_fd.get()); + return 1; + } + android::base::WriteStringToFd("100\n", status_fd.get()); + return 0; +} + +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; + } + 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; + } + 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; + } + android::base::WriteStringToFd("100\n", status_fd.get()); + return 0; +} +static int read_bcb() { + bootloader_message boot; + if (read_bootloader_message(&boot) != 0) { + ALOGE("failed to get bootloader message"); + return 1; + } + printf("bcb command: %s\n", boot.command); + printf("bcb recovery:\n%s\n", boot.recovery); + return 0; +} + +static void usage(const char* exename) { + fprintf(stderr, "Usage of %s:\n", exename); + fprintf(stderr, "%s [ ] Uncrypt ota package.\n", exename); + fprintf(stderr, "%s --reboot Clear BCB data and reboot to recovery.\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); + fprintf(stderr, "%s --read-bcb Read BCB data from misc partition.\n", exename); +} + +int main(int argc, char** argv) { + if (argc == 2) { + if (strcmp(argv[1], "--reboot") == 0) { + reboot_to_recovery(); + } else 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) { - // 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)) { - android::base::WriteStringToFd("-1\n", status_fd); - return 1; - } - input_path = package.c_str(); - map_file = cache_block_map.c_str(); } - - int status = uncrypt(input_path, map_file, status_fd); - if (status != 0) { - android::base::WriteStringToFd("-1\n", status_fd); - return 1; - } - - android::base::WriteStringToFd("100\n", status_fd); + return uncrypt_wrapper(input_path, map_file, STATUS_FILE); } - - return 0; + usage(argv[0]); + return 2; } diff --git a/uncrypt/uncrypt.rc b/uncrypt/uncrypt.rc index 5f4c47936..b07c1dada 100644 --- a/uncrypt/uncrypt.rc +++ b/uncrypt/uncrypt.rc @@ -7,3 +7,13 @@ service pre-recovery /system/bin/uncrypt --reboot class main disabled oneshot + +service setup-bcb /system/bin/uncrypt --setup-bcb + class main + disabled + oneshot + +service clear-bcb /system/bin/uncrypt --clear-bcb + class main + disabled + oneshot \ No newline at end of file -- cgit v1.2.3 From 5b3b373a4938b8359bb55c62057fc5771e689add Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 22 Feb 2016 17:33:14 -0800 Subject: uncrypt: Retire pre-recovery service. The framework CL in [1] removes the use of "pre-recovery" service which is basically to trigger a reboot into the recovery. [1] commit e8a403d57c8ea540f8287cdaee8b90f0cf9626a3 Bug: 26830925 Change-Id: I131f31a228df59e4f9c3024b238bbdee0be2b157 --- uncrypt/uncrypt.cpp | 14 +------------- uncrypt/uncrypt.rc | 7 +------ 2 files changed, 2 insertions(+), 19 deletions(-) (limited to 'uncrypt') diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index 705744eb6..2a32108a3 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -406,15 +406,6 @@ static int write_bootloader_message(const bootloader_message* in) { return 0; } -static void reboot_to_recovery() { - ALOGI("rebooting to recovery"); - property_set("sys.powerctl", "reboot,recovery"); - while (true) { - pause(); - } - ALOGE("reboot didn't succeed?"); -} - static int uncrypt(const char* input_path, const char* map_file, int status_fd) { ALOGI("update package is \"%s\"", input_path); @@ -543,7 +534,6 @@ static int read_bcb() { static void usage(const char* exename) { fprintf(stderr, "Usage of %s:\n", exename); fprintf(stderr, "%s [ ] Uncrypt ota package.\n", exename); - fprintf(stderr, "%s --reboot Clear BCB data and reboot to recovery.\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); fprintf(stderr, "%s --read-bcb Read BCB data from misc partition.\n", exename); @@ -551,9 +541,7 @@ static void usage(const char* exename) { int main(int argc, char** argv) { if (argc == 2) { - if (strcmp(argv[1], "--reboot") == 0) { - reboot_to_recovery(); - } else if (strcmp(argv[1], "--clear-bcb") == 0) { + 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); diff --git a/uncrypt/uncrypt.rc b/uncrypt/uncrypt.rc index b07c1dada..d5d803b9f 100644 --- a/uncrypt/uncrypt.rc +++ b/uncrypt/uncrypt.rc @@ -3,11 +3,6 @@ service uncrypt /system/bin/uncrypt disabled oneshot -service pre-recovery /system/bin/uncrypt --reboot - class main - disabled - oneshot - service setup-bcb /system/bin/uncrypt --setup-bcb class main disabled @@ -16,4 +11,4 @@ service setup-bcb /system/bin/uncrypt --setup-bcb service clear-bcb /system/bin/uncrypt --clear-bcb class main disabled - oneshot \ No newline at end of file + oneshot -- cgit v1.2.3 From 3a2bb594df4b48c6afb1f029041dd6db0735de58 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 25 Feb 2016 12:38:53 -0800 Subject: uncrypt: Communicate via /dev/socket/uncrypt. We used to rely on files (e.g. /cache/recovery/command and /cache/recovery/uncrypt_status) to communicate between uncrypt and its caller (i.e. system_server). Since A/B devices may not have /cache partitions anymore, we switch to socket communication instead. We will keep the use of /cache/recovery/uncrypt_file to indicate the OTA package to be uncrypt'd though. Because there is existing logic in ShutdownThread.java that depends on the existence of the file to detect pending uncrypt works. This part won't affect A/B devices without /cache partitions, because such devices won't need uncrypt service (i.e the real de-encrypt work) anyway. Bug: 27176738 Change-Id: I481406e09e3ffc7b80f2c9e39003b9fca028742e --- uncrypt/uncrypt.cpp | 260 ++++++++++++++++++++++++++++++++++++---------------- uncrypt/uncrypt.rc | 3 + 2 files changed, 184 insertions(+), 79 deletions(-) (limited to 'uncrypt') 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. + * a9. + * c10. + * 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 #include #include #include @@ -49,6 +96,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +111,7 @@ #include #include #include +#include #include #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(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 -- cgit v1.2.3 From 61799baba3631f55469d2754542130255ce790cf Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Tue, 29 Mar 2016 14:33:35 -0700 Subject: uncrypt: remove --read-bcb option. Bug: 27897241 Change-Id: I4f52ada58e8f204dba8c974ea0ae03876411ecf0 --- uncrypt/uncrypt.cpp | 32 -------------------------------- 1 file changed, 32 deletions(-) (limited to 'uncrypt') diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index e783b9e7a..8003f9029 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -425,24 +425,6 @@ static std::string get_misc_blk_device() { return ""; } -static int read_bootloader_message(bootloader_message* out) { - std::string misc_blk_device = get_misc_blk_device(); - if (misc_blk_device.empty()) { - ALOGE("failed to find /misc partition."); - return -1; - } - unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY)); - if (!fd) { - ALOGE("failed to open %s: %s", misc_blk_device.c_str(), strerror(errno)); - return -1; - } - if (!android::base::ReadFully(fd.get(), out, sizeof(*out))) { - ALOGE("failed to read %s: %s", misc_blk_device.c_str(), strerror(errno)); - return -1; - } - return 0; -} - static int write_bootloader_message(const bootloader_message* in) { std::string misc_blk_device = get_misc_blk_device(); if (misc_blk_device.empty()) { @@ -570,23 +552,11 @@ static bool setup_bcb(const int socket) { return true; } -static int read_bcb() { - bootloader_message boot; - if (read_bootloader_message(&boot) != 0) { - ALOGE("failed to get bootloader message"); - return 1; - } - printf("bcb command: %s\n", boot.command); - printf("bcb recovery:\n%s\n", boot.recovery); - return 0; -} - static void usage(const char* exename) { fprintf(stderr, "Usage of %s:\n", exename); fprintf(stderr, "%s [ ] 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); - fprintf(stderr, "%s --read-bcb Read BCB data from misc partition.\n", exename); } int main(int argc, char** argv) { @@ -598,8 +568,6 @@ int main(int argc, char** argv) { 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) { -- cgit v1.2.3 From ffa3a1c222c1a8b0296f97dfa3efb35108ac6d60 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Tue, 29 Mar 2016 15:35:58 -0700 Subject: uncrypt: fix call to close(). Bug: 27897229 Change-Id: Iab5e829af1676f7fcd8a4b00a194aa679ed4e372 --- uncrypt/uncrypt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'uncrypt') diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp index e783b9e7a..ee4d616a7 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -372,7 +372,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* ALOGE("failed to fsync \"%s\": %s", tmp_map_file.c_str(), strerror(errno)); return -1; } - if (close(mapfd.get() == -1)) { + if (close(mapfd.get()) == -1) { ALOGE("failed to close %s: %s", tmp_map_file.c_str(), strerror(errno)); return -1; } @@ -406,7 +406,7 @@ static int produce_block_map(const char* path, const char* map_file, const char* ALOGE("failed to fsync %s: %s", dir_name.c_str(), strerror(errno)); return -1; } - if (close(dfd.get() == -1)) { + if (close(dfd.get()) == -1) { ALOGE("failed to close %s: %s", dir_name.c_str(), strerror(errno)); return -1; } -- cgit v1.2.3 From a58a6dbe3d06aace0d1419838e162aa5267a4fc0 Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Wed, 6 Apr 2016 15:52:18 -0700 Subject: uncrypt: split libbootloader_message_writer for reuse. init and vold also need to write bootloader message, so split this function from uncrypt into a separate library. Bug: 27176738 Change-Id: If9b0887b4f6ffab6162d9cb47a6ceb7eedd60b4d --- uncrypt/Android.mk | 12 +++- uncrypt/bootloader_message_writer.cpp | 107 ++++++++++++++++++++++++++++ uncrypt/include/bootloader_message_writer.h | 35 +++++++++ uncrypt/uncrypt.cpp | 50 ++----------- 4 files changed, 160 insertions(+), 44 deletions(-) create mode 100644 uncrypt/bootloader_message_writer.cpp create mode 100644 uncrypt/include/bootloader_message_writer.h (limited to 'uncrypt') diff --git a/uncrypt/Android.mk b/uncrypt/Android.mk index 6422cb2f4..09cfdfca5 100644 --- a/uncrypt/Android.mk +++ b/uncrypt/Android.mk @@ -14,6 +14,15 @@ 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 @@ -24,7 +33,8 @@ 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 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 +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#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& 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 +#include + +bool clear_bootloader_message(std::string* err); + +bool write_bootloader_message(const std::vector& options, std::string* err); + +#else +#include + +// 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 2ef093579..d7105a01f 100644 --- a/uncrypt/uncrypt.cpp +++ b/uncrypt/uncrypt.cpp @@ -109,6 +109,7 @@ #include #include #include +#include #include #include #include @@ -117,7 +118,6 @@ #define LOG_TAG "uncrypt" #include -#include "bootloader.h" #include "unique_fd.h" #define WINDOW_SIZE 5 @@ -414,40 +414,6 @@ static int produce_block_map(const char* path, const char* map_file, const char* return 0; } -static std::string get_misc_blk_device() { - if (fstab == nullptr) { - return ""; - } - struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, "/misc"); - if (rec != nullptr) { - return rec->blk_device; - } - return ""; -} - -static int write_bootloader_message(const bootloader_message* in) { - std::string misc_blk_device = get_misc_blk_device(); - if (misc_blk_device.empty()) { - ALOGE("failed to find /misc partition."); - return -1; - } - unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY | O_SYNC)); - if (!fd) { - ALOGE("failed to open %s: %s", misc_blk_device.c_str(), strerror(errno)); - return -1; - } - if (!android::base::WriteFully(fd.get(), in, sizeof(*in))) { - ALOGE("failed to write %s: %s", misc_blk_device.c_str(), strerror(errno)); - return -1; - } - // TODO: O_SYNC and fsync() duplicates each other? - if (fsync(fd.get()) == -1) { - ALOGE("failed to fsync %s: %s", misc_blk_device.c_str(), strerror(errno)); - return -1; - } - return 0; -} - static int uncrypt(const char* input_path, const char* map_file, const int socket) { ALOGI("update package is \"%s\"", input_path); @@ -510,8 +476,9 @@ static bool uncrypt_wrapper(const char* input_path, const char* map_file, const } static bool clear_bcb(const int socket) { - bootloader_message boot = {}; - if (write_bootloader_message(&boot) != 0) { + 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; } @@ -538,12 +505,9 @@ static bool setup_bcb(const int socket) { 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"); + 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; } -- cgit v1.2.3