diff options
Diffstat (limited to 'applypatch/applypatch.cpp')
-rw-r--r-- | applypatch/applypatch.cpp | 320 |
1 files changed, 209 insertions, 111 deletions
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 7645a4005..5c6c83f4f 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -38,6 +38,9 @@ #include <android-base/strings.h> #include <openssl/sha.h> +#include "bmlutils/bmlutils.h" +#include "mtdutils/mtdutils.h" + #include "edify/expr.h" #include "otafault/ota_io.h" #include "otautil/cache_location.h" @@ -49,11 +52,16 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr const std::string& target_filename, const uint8_t target_sha1[SHA_DIGEST_LENGTH], const Value* bonus_data); +static bool mtd_partitions_scanned = false; + // Read a file into memory; store the file contents and associated metadata in *file. // Return 0 on success. int LoadFileContents(const char* filename, FileContents* file) { - // A special 'filename' beginning with "EMMC:" means to load the contents of a partition. - if (strncmp(filename, "EMMC:", 5) == 0) { + // A special 'filename' beginning with "MTD:" or "EMMC:" means to + // load the contents of a partition. + if (strncmp(filename, "MTD:", 4) == 0 || + strncmp(filename, "EMMC:", 5) == 0 || + strncmp(filename, "BML:", 4) == 0) { return LoadPartitionContents(filename, file); } @@ -94,13 +102,27 @@ int LoadFileContents(const char* filename, FileContents* file) { // "end-of-file" marker), so the caller must specify the possible // lengths and the hash of the data, and we'll do the load expecting // to find one of those hashes. +enum PartitionType { MTD, EMMC }; + static int LoadPartitionContents(const std::string& filename, FileContents* file) { std::vector<std::string> pieces = android::base::Split(filename, ":"); - if (pieces.size() < 4 || pieces.size() % 2 != 0 || pieces[0] != "EMMC") { + if (pieces.size() < 4 || pieces.size() % 2 != 0) { printf("LoadPartitionContents called with bad filename \"%s\"\n", filename.c_str()); return -1; } + enum PartitionType type; + if (pieces[0] == "MTD") { + type = MTD; + } else if (pieces[0] == "EMMC") { + type = EMMC; + } else if (pieces[0] == "BML") { + type = EMMC; + } else { + printf("LoadPartitionContents called with bad filename (%s)\n", filename.c_str()); + return -1; + } + size_t pair_count = (pieces.size() - 2) / 2; // # of (size, sha1) pairs in filename std::vector<std::pair<size_t, std::string>> pairs; for (size_t i = 0; i < pair_count; ++i) { @@ -149,6 +171,14 @@ static int LoadPartitionContents(const std::string& filename, FileContents* file buffer_ptr += read; } + if (pieces[0] == "BML") { + if (strcmp(partition, "boot") == 0) { + partition = BOARD_BML_BOOT; + } else if (strcmp(partition, "recovery") == 0) { + partition = BOARD_BML_RECOVERY; + } + } + // Duplicate the SHA context and finalize the duplicate so we can // check it against this pair's expected hash. SHA_CTX temp_ctx; @@ -216,131 +246,199 @@ int SaveFileContents(const char* filename, const FileContents* file) { // might contain multiple colons, but WriteToPartition() only uses the first // two and ignores the rest. Return 0 on success. int WriteToPartition(const unsigned char* data, size_t len, const std::string& target) { - std::vector<std::string> pieces = android::base::Split(target, ":"); - if (pieces.size() < 2 || pieces[0] != "EMMC") { - printf("WriteToPartition called with bad target (%s)\n", target.c_str()); - return -1; - } - - const char* partition = pieces[1].c_str(); - unique_fd fd(ota_open(partition, O_RDWR)); - if (fd == -1) { - printf("failed to open %s: %s\n", partition, strerror(errno)); - return -1; - } + std::string copy(target); + std::vector<std::string> pieces = android::base::Split(copy, ":"); - size_t start = 0; - bool success = false; - for (size_t attempt = 0; attempt < 2; ++attempt) { - if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) { - printf("failed seek on %s: %s\n", partition, strerror(errno)); - return -1; - } - while (start < len) { - size_t to_write = len - start; - if (to_write > 1 << 20) to_write = 1 << 20; - - ssize_t written = TEMP_FAILURE_RETRY(ota_write(fd, data + start, to_write)); - if (written == -1) { - printf("failed write writing to %s: %s\n", partition, strerror(errno)); + if (pieces.size() < 2) { + printf("WriteToPartition called with bad target (%s)\n", target.c_str()); return -1; - } - start += written; - } - - if (ota_fsync(fd) != 0) { - printf("failed to sync to %s: %s\n", partition, strerror(errno)); - return -1; - } - if (ota_close(fd) != 0) { - printf("failed to close %s: %s\n", partition, strerror(errno)); - return -1; - } - - fd.reset(ota_open(partition, O_RDONLY)); - if (fd == -1) { - printf("failed to reopen %s for verify: %s\n", partition, strerror(errno)); - return -1; } - // Drop caches so our subsequent verification read won't just be reading the cache. - sync(); - unique_fd dc(ota_open("/proc/sys/vm/drop_caches", O_WRONLY)); - if (TEMP_FAILURE_RETRY(ota_write(dc, "3\n", 2)) == -1) { - printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno)); + enum PartitionType type; + if (pieces[0] == "MTD") { + type = MTD; + } else if (pieces[0] == "EMMC") { + type = EMMC; + } else if (pieces[0] == "BML") { + type = EMMC; } else { - printf(" caches dropped\n"); - } - ota_close(dc); - sleep(1); - - // Verify. - if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) { - printf("failed to seek back to beginning of %s: %s\n", partition, strerror(errno)); - return -1; + printf("WriteToPartition called with bad target (%s)\n", target.c_str()); + return -1; } - unsigned char buffer[4096]; - start = len; - for (size_t p = 0; p < len; p += sizeof(buffer)) { - size_t to_read = len - p; - if (to_read > sizeof(buffer)) { - to_read = sizeof(buffer); - } + const char* partition = pieces[1].c_str(); - size_t so_far = 0; - while (so_far < to_read) { - ssize_t read_count = TEMP_FAILURE_RETRY(ota_read(fd, buffer + so_far, to_read - so_far)); - if (read_count == -1) { - printf("verify read error %s at %zu: %s\n", partition, p, strerror(errno)); - return -1; - } else if (read_count == 0) { - printf("verify read reached unexpected EOF, %s at %zu\n", partition, p); - return -1; - } - if (static_cast<size_t>(read_count) < to_read) { - printf("short verify read %s at %zu: %zd %zu\n", partition, p, read_count, to_read); + if (pieces[0] == "BML") { + if (strcmp(partition, "boot") == 0) { + partition = BOARD_BML_BOOT; + } else if (strcmp(partition, "recovery") == 0) { + partition = BOARD_BML_RECOVERY; } - so_far += read_count; - } - if (memcmp(buffer, data + p, to_read) != 0) { - printf("verification failed starting at %zu\n", p); - start = p; - break; - } + int bmlpartition = open(partition, O_RDWR | O_LARGEFILE); + if (bmlpartition < 0) + return -1; + if (ioctl(bmlpartition, BML_UNLOCK_ALL, 0)) { + printf("failed to unlock BML partition: (%s)\n", partition); + return -1; + } + close(bmlpartition); } - if (start == len) { - printf("verification read succeeded (attempt %zu)\n", attempt + 1); - success = true; - break; + if (partition == NULL) { + printf("bad partition target name \"%s\"\n", target.c_str()); + return -1; } - if (ota_close(fd) != 0) { - printf("failed to close %s: %s\n", partition, strerror(errno)); - return -1; - } + switch (type) { + case MTD: { + if (!mtd_partitions_scanned) { + mtd_scan_partitions(); + mtd_partitions_scanned = true; + } + + const MtdPartition* mtd = mtd_find_partition_by_name(partition); + if (mtd == NULL) { + printf("mtd partition \"%s\" not found for writing\n", partition); + return -1; + } + + MtdWriteContext* ctx = mtd_write_partition(mtd); + if (ctx == NULL) { + printf("failed to init mtd partition \"%s\" for writing\n", partition); + return -1; + } + + size_t written = mtd_write_data(ctx, reinterpret_cast<const char*>(data), len); + if (written != len) { + printf("only wrote %zu of %zu bytes to MTD %s\n", written, len, partition); + mtd_write_close(ctx); + return -1; + } + + if (mtd_erase_blocks(ctx, -1) < 0) { + printf("error finishing mtd write of %s\n", partition); + mtd_write_close(ctx); + return -1; + } + + if (mtd_write_close(ctx)) { + printf("error closing mtd write of %s\n", partition); + return -1; + } + break; + } - fd.reset(ota_open(partition, O_RDWR)); - if (fd == -1) { - printf("failed to reopen %s for retry write && verify: %s\n", partition, strerror(errno)); - return -1; + case EMMC: { + size_t start = 0; + bool success = false; + unique_fd fd(ota_open(partition, O_RDWR | O_SYNC)); + if (fd < 0) { + printf("failed to open %s: %s\n", partition, strerror(errno)); + return -1; + } + + for (size_t attempt = 0; attempt < 2; ++attempt) { + if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) { + printf("failed seek on %s: %s\n", partition, strerror(errno)); + return -1; + } + while (start < len) { + size_t to_write = len - start; + if (to_write > 1<<20) to_write = 1<<20; + + ssize_t written = TEMP_FAILURE_RETRY(ota_write(fd, data+start, to_write)); + if (written == -1) { + printf("failed write writing to %s: %s\n", partition, strerror(errno)); + return -1; + } + start += written; + } + if (ota_fsync(fd) != 0) { + printf("failed to sync to %s (%s)\n", partition, strerror(errno)); + return -1; + } + if (ota_close(fd) != 0) { + printf("failed to close %s (%s)\n", partition, strerror(errno)); + return -1; + } + unique_fd fd(ota_open(partition, O_RDONLY)); + if (fd < 0) { + printf("failed to reopen %s for verify (%s)\n", partition, strerror(errno)); + return -1; + } + + // Drop caches so our subsequent verification read + // won't just be reading the cache. + sync(); + unique_fd dc(ota_open("/proc/sys/vm/drop_caches", O_WRONLY)); + if (TEMP_FAILURE_RETRY(ota_write(dc, "3\n", 2)) == -1) { + printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno)); + } else { + printf(" caches dropped\n"); + } + ota_close(dc); + sleep(1); + + // verify + if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) { + printf("failed to seek back to beginning of %s: %s\n", + partition, strerror(errno)); + return -1; + } + unsigned char buffer[4096]; + start = len; + for (size_t p = 0; p < len; p += sizeof(buffer)) { + size_t to_read = len - p; + if (to_read > sizeof(buffer)) { + to_read = sizeof(buffer); + } + + size_t so_far = 0; + while (so_far < to_read) { + ssize_t read_count = + TEMP_FAILURE_RETRY(ota_read(fd, buffer+so_far, to_read-so_far)); + if (read_count == -1) { + printf("verify read error %s at %zu: %s\n", + partition, p, strerror(errno)); + return -1; + } + if (static_cast<size_t>(read_count) < to_read) { + printf("short verify read %s at %zu: %zd %zu %s\n", + partition, p, read_count, to_read, strerror(errno)); + } + so_far += read_count; + } + + if (memcmp(buffer, data+p, to_read) != 0) { + printf("verification failed starting at %zu\n", p); + start = p; + break; + } + } + + if (start == len) { + printf("verification read succeeded (attempt %zu)\n", attempt+1); + success = true; + break; + } + } + + if (!success) { + printf("failed to verify after all attempts\n"); + return -1; + } + + if (ota_close(fd) != 0) { + printf("error closing %s (%s)\n", partition, strerror(errno)); + return -1; + } + sync(); + break; + } } - } - - if (!success) { - printf("failed to verify after all attempts\n"); - return -1; - } - if (ota_close(fd) == -1) { - printf("error closing %s: %s\n", partition, strerror(errno)); - return -1; - } - sync(); - - return 0; + return 0; } // Take a string 'str' of 40 hex digits and parse it into the 20 |