Merge commit 'ce46828e08281dc507d4e40ba9e8b770bc21cf0b' into HEAD
diff --git a/Android.mk b/Android.mk
index 602a856..4da34ee 100644
--- a/Android.mk
+++ b/Android.mk
@@ -104,28 +104,10 @@
 LOCAL_MODULE := libverifier
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := \
-    asn1_decoder.cpp
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_MODULE := verifier_test
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_TAGS := tests
-LOCAL_CFLAGS += -Wno-unused-parameter
-LOCAL_SRC_FILES := \
-    verifier_test.cpp \
     asn1_decoder.cpp \
     verifier.cpp \
     ui.cpp
-LOCAL_STATIC_LIBRARIES := \
-    libmincrypt \
-    libminui \
-    libminzip \
-    libcutils \
-    libc
-include $(BUILD_EXECUTABLE)
-
+include $(BUILD_STATIC_LIBRARY)
 
 include $(LOCAL_PATH)/minui/Android.mk \
     $(LOCAL_PATH)/minzip/Android.mk \
@@ -135,6 +117,7 @@
     $(LOCAL_PATH)/tools/Android.mk \
     $(LOCAL_PATH)/edify/Android.mk \
     $(LOCAL_PATH)/uncrypt/Android.mk \
+    $(LOCAL_PATH)/otafault/Android.mk \
     $(LOCAL_PATH)/updater/Android.mk \
     $(LOCAL_PATH)/update_verifier/Android.mk \
     $(LOCAL_PATH)/applypatch/Android.mk
diff --git a/README.md b/README.md
index bab7e87..01fab94 100644
--- a/README.md
+++ b/README.md
@@ -10,3 +10,20 @@
     # without flashing the recovery partition:
     adb reboot bootloader
     fastboot boot $ANDROID_PRODUCT_OUT/recovery.img
+
+Running the tests
+-----------------
+    # After setting up environment and lunch.
+    mmma -j bootable/recovery
+
+    # Running the tests on device.
+    adb root
+    adb sync data
+
+    # 32-bit device
+    adb shell /data/nativetest/recovery_unit_test/recovery_unit_test
+    adb shell /data/nativetest/recovery_component_test/recovery_component_test
+
+    # Or 64-bit device
+    adb shell /data/nativetest64/recovery_unit_test/recovery_unit_test
+    adb shell /data/nativetest64/recovery_component_test/recovery_component_test
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
index 93a2729..90a86dc 100644
--- a/applypatch/Android.mk
+++ b/applypatch/Android.mk
@@ -20,18 +20,42 @@
 LOCAL_SRC_FILES := applypatch.cpp bspatch.cpp freecache.cpp imgpatch.cpp utils.cpp
 LOCAL_MODULE := libapplypatch
 LOCAL_MODULE_TAGS := eng
-LOCAL_C_INCLUDES += external/bzip2 external/zlib bootable/recovery
-LOCAL_STATIC_LIBRARIES += libbase libmtdutils libmincrypt libbz libz
+LOCAL_C_INCLUDES += bootable/recovery
+LOCAL_STATIC_LIBRARIES += libbase libotafault libmtdutils libcrypto_static libbz libz
 
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 
 LOCAL_CLANG := true
+LOCAL_SRC_FILES := bspatch.cpp imgpatch.cpp utils.cpp
+LOCAL_MODULE := libimgpatch
+LOCAL_C_INCLUDES += bootable/recovery
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES += libcrypto_static libbz libz
+
+include $(BUILD_STATIC_LIBRARY)
+
+ifeq ($(HOST_OS),linux)
+include $(CLEAR_VARS)
+
+LOCAL_CLANG := true
+LOCAL_SRC_FILES := bspatch.cpp imgpatch.cpp utils.cpp
+LOCAL_MODULE := libimgpatch
+LOCAL_C_INCLUDES += bootable/recovery
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES += libcrypto_static libbz libz
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+endif  # HOST_OS == linux
+
+include $(CLEAR_VARS)
+
+LOCAL_CLANG := true
 LOCAL_SRC_FILES := main.cpp
 LOCAL_MODULE := applypatch
 LOCAL_C_INCLUDES += bootable/recovery
-LOCAL_STATIC_LIBRARIES += libapplypatch libbase libmtdutils libmincrypt libbz
+LOCAL_STATIC_LIBRARIES += libapplypatch libbase libotafault libmtdutils libcrypto_static libbz libedify
 LOCAL_SHARED_LIBRARIES += libz libcutils libc
 
 include $(BUILD_EXECUTABLE)
diff --git a/applypatch/Makefile b/applypatch/Makefile
new file mode 100644
index 0000000..fa6298d
--- /dev/null
+++ b/applypatch/Makefile
@@ -0,0 +1,32 @@
+# 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.
+
+# This file is for building imgdiff in Chrome OS.
+
+CPPFLAGS += -iquote..
+CXXFLAGS += -std=c++11 -O3 -Wall -Werror
+LDLIBS += -lbz2 -lz
+
+.PHONY: all clean
+
+all: imgdiff libimgpatch.a
+
+clean:
+	rm -f *.o imgdiff libimgpatch.a
+
+imgdiff: imgdiff.o bsdiff.o utils.o
+	$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDLIBS) -o $@ $^
+
+libimgpatch.a: imgpatch.o bspatch.o utils.o
+	${AR} rcs $@ $^
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index f9425af..f66dbe3 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -25,13 +25,17 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <memory>
+#include <string>
+
 #include <android-base/strings.h>
 
-#include "mincrypt/sha.h"
+#include "openssl/sha.h"
 #include "applypatch.h"
 #include "mtdutils/mtdutils.h"
 #include "edify/expr.h"
 #include "print_sha1.h"
+#include "otafault/ota_io.h"
 
 static int LoadPartitionContents(const char* filename, FileContents* file);
 static ssize_t FileSink(const unsigned char* data, ssize_t len, void* token);
@@ -41,7 +45,7 @@
                           const Value* copy_patch_value,
                           const char* source_filename,
                           const char* target_filename,
-                          const uint8_t target_sha1[SHA_DIGEST_SIZE],
+                          const uint8_t target_sha1[SHA_DIGEST_LENGTH],
                           size_t target_size,
                           const Value* bonus_data);
 
@@ -52,8 +56,6 @@
 //
 // Return 0 on success.
 int LoadFileContents(const char* filename, FileContents* file) {
-    file->data = NULL;
-
     // A special 'filename' beginning with "MTD:" or "EMMC:" means to
     // load the contents of a partition.
     if (strncmp(filename, "MTD:", 4) == 0 ||
@@ -66,27 +68,22 @@
         return -1;
     }
 
-    file->size = file->st.st_size;
-    file->data = reinterpret_cast<unsigned char*>(malloc(file->size));
-
-    FILE* f = fopen(filename, "rb");
+    std::vector<unsigned char> data(file->st.st_size);
+    FILE* f = ota_fopen(filename, "rb");
     if (f == NULL) {
         printf("failed to open \"%s\": %s\n", filename, strerror(errno));
-        free(file->data);
-        file->data = NULL;
         return -1;
     }
 
-    size_t bytes_read = fread(file->data, 1, file->size, f);
-    if (bytes_read != static_cast<size_t>(file->size)) {
-        printf("short read of \"%s\" (%zu bytes of %zd)\n", filename, bytes_read, file->size);
-        free(file->data);
-        file->data = NULL;
+    size_t bytes_read = ota_fread(data.data(), 1, data.size(), f);
+    if (bytes_read != data.size()) {
+        printf("short read of \"%s\" (%zu bytes of %zu)\n", filename, bytes_read, data.size());
+        ota_fclose(f);
         return -1;
     }
-    fclose(f);
-
-    SHA_hash(file->data, file->size, file->sha1);
+    ota_fclose(f);
+    file->data = std::move(data);
+    SHA1(file->data.data(), file->data.size(), file->sha1);
     return 0;
 }
 
@@ -173,7 +170,7 @@
         }
 
         case EMMC:
-            dev = fopen(partition, "rb");
+            dev = ota_fopen(partition, "rb");
             if (dev == NULL) {
                 printf("failed to open emmc partition \"%s\": %s\n", partition, strerror(errno));
                 return -1;
@@ -181,55 +178,53 @@
     }
 
     SHA_CTX sha_ctx;
-    SHA_init(&sha_ctx);
-    uint8_t parsed_sha[SHA_DIGEST_SIZE];
+    SHA1_Init(&sha_ctx);
+    uint8_t parsed_sha[SHA_DIGEST_LENGTH];
 
     // Allocate enough memory to hold the largest size.
-    file->data = reinterpret_cast<unsigned char*>(malloc(size[index[pairs-1]]));
-    char* p = (char*)file->data;
-    file->size = 0;                // # bytes read so far
+    std::vector<unsigned char> data(size[index[pairs-1]]);
+    char* p = reinterpret_cast<char*>(data.data());
+    size_t data_size = 0;                // # bytes read so far
     bool found = false;
 
     for (size_t i = 0; i < pairs; ++i) {
         // Read enough additional bytes to get us up to the next size. (Again,
         // we're trying the possibilities in order of increasing size).
-        size_t next = size[index[i]] - file->size;
-        size_t read = 0;
+        size_t next = size[index[i]] - data_size;
         if (next > 0) {
+            size_t read = 0;
             switch (type) {
                 case MTD:
                     read = mtd_read_data(ctx, p, next);
                     break;
 
                 case EMMC:
-                    read = fread(p, 1, next, dev);
+                    read = ota_fread(p, 1, next, dev);
                     break;
             }
             if (next != read) {
                 printf("short read (%zu bytes of %zu) for partition \"%s\"\n",
                        read, next, partition);
-                free(file->data);
-                file->data = NULL;
                 return -1;
             }
-            SHA_update(&sha_ctx, p, read);
-            file->size += read;
+            SHA1_Update(&sha_ctx, p, read);
+            data_size += read;
+            p += read;
         }
 
         // Duplicate the SHA context and finalize the duplicate so we can
         // check it against this pair's expected hash.
         SHA_CTX temp_ctx;
         memcpy(&temp_ctx, &sha_ctx, sizeof(SHA_CTX));
-        const uint8_t* sha_so_far = SHA_final(&temp_ctx);
+        uint8_t sha_so_far[SHA_DIGEST_LENGTH];
+        SHA1_Final(sha_so_far, &temp_ctx);
 
         if (ParseSha1(sha1sum[index[i]].c_str(), parsed_sha) != 0) {
             printf("failed to parse sha1 %s in %s\n", sha1sum[index[i]].c_str(), filename);
-            free(file->data);
-            file->data = NULL;
             return -1;
         }
 
-        if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_SIZE) == 0) {
+        if (memcmp(sha_so_far, parsed_sha, SHA_DIGEST_LENGTH) == 0) {
             // we have a match.  stop reading the partition; we'll return
             // the data we've read so far.
             printf("partition read matched size %zu sha %s\n",
@@ -237,8 +232,6 @@
             found = true;
             break;
         }
-
-        p += read;
     }
 
     switch (type) {
@@ -247,7 +240,7 @@
             break;
 
         case EMMC:
-            fclose(dev);
+            ota_fclose(dev);
             break;
     }
 
@@ -255,16 +248,13 @@
     if (!found) {
         // Ran off the end of the list of (size,sha1) pairs without finding a match.
         printf("contents of partition \"%s\" didn't match %s\n", partition, filename);
-        free(file->data);
-        file->data = NULL;
         return -1;
     }
 
-    const uint8_t* sha_final = SHA_final(&sha_ctx);
-    for (size_t i = 0; i < SHA_DIGEST_SIZE; ++i) {
-        file->sha1[i] = sha_final[i];
-    }
+    SHA1_Final(file->sha1, &sha_ctx);
 
+    data.resize(data_size);
+    file->data = std::move(data);
     // Fake some stat() info.
     file->st.st_mode = 0644;
     file->st.st_uid = 0;
@@ -277,24 +267,24 @@
 // Save the contents of the given FileContents object under the given
 // filename.  Return 0 on success.
 int SaveFileContents(const char* filename, const FileContents* file) {
-    int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR);
+    int fd = ota_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR);
     if (fd < 0) {
         printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno));
         return -1;
     }
 
-    ssize_t bytes_written = FileSink(file->data, file->size, &fd);
-    if (bytes_written != file->size) {
-        printf("short write of \"%s\" (%zd bytes of %zd) (%s)\n",
-               filename, bytes_written, file->size, strerror(errno));
-        close(fd);
+    ssize_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd);
+    if (bytes_written != static_cast<ssize_t>(file->data.size())) {
+        printf("short write of \"%s\" (%zd bytes of %zu) (%s)\n",
+               filename, bytes_written, file->data.size(), strerror(errno));
+        ota_close(fd);
         return -1;
     }
-    if (fsync(fd) != 0) {
+    if (ota_fsync(fd) != 0) {
         printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno));
         return -1;
     }
-    if (close(fd) != 0) {
+    if (ota_close(fd) != 0) {
         printf("close of \"%s\" failed: %s\n", filename, strerror(errno));
         return -1;
     }
@@ -315,7 +305,7 @@
 // "MTD:<partition>[:...]" or "EMMC:<partition_device>[:...]". The target name
 // might contain multiple colons, but WriteToPartition() only uses the first
 // two and ignores the rest. Return 0 on success.
-int WriteToPartition(unsigned char* data, size_t len, const char* target) {
+int WriteToPartition(const unsigned char* data, size_t len, const char* target) {
     std::string copy(target);
     std::vector<std::string> pieces = android::base::Split(copy, ":");
 
@@ -354,7 +344,7 @@
                 return -1;
             }
 
-            size_t written = mtd_write_data(ctx, reinterpret_cast<char*>(data), len);
+            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);
@@ -377,7 +367,7 @@
         case EMMC: {
             size_t start = 0;
             bool success = false;
-            int fd = open(partition, O_RDWR | O_SYNC);
+            int fd = ota_open(partition, O_RDWR | O_SYNC);
             if (fd < 0) {
                 printf("failed to open %s: %s\n", partition, strerror(errno));
                 return -1;
@@ -392,22 +382,22 @@
                     size_t to_write = len - start;
                     if (to_write > 1<<20) to_write = 1<<20;
 
-                    ssize_t written = TEMP_FAILURE_RETRY(write(fd, data+start, to_write));
+                    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 (fsync(fd) != 0) {
+                if (ota_fsync(fd) != 0) {
                    printf("failed to sync to %s (%s)\n", partition, strerror(errno));
                    return -1;
                 }
-                if (close(fd) != 0) {
+                if (ota_close(fd) != 0) {
                    printf("failed to close %s (%s)\n", partition, strerror(errno));
                    return -1;
                 }
-                fd = open(partition, O_RDONLY);
+                fd = ota_open(partition, O_RDONLY);
                 if (fd < 0) {
                    printf("failed to reopen %s for verify (%s)\n", partition, strerror(errno));
                    return -1;
@@ -416,13 +406,13 @@
                 // Drop caches so our subsequent verification read
                 // won't just be reading the cache.
                 sync();
-                int dc = open("/proc/sys/vm/drop_caches", O_WRONLY);
-                if (TEMP_FAILURE_RETRY(write(dc, "3\n", 2)) == -1) {
+                int 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");
                 }
-                close(dc);
+                ota_close(dc);
                 sleep(1);
 
                 // verify
@@ -442,7 +432,7 @@
                     size_t so_far = 0;
                     while (so_far < to_read) {
                         ssize_t read_count =
-                                TEMP_FAILURE_RETRY(read(fd, buffer+so_far, to_read-so_far));
+                                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));
@@ -474,7 +464,7 @@
                 return -1;
             }
 
-            if (close(fd) != 0) {
+            if (ota_close(fd) != 0) {
                 printf("error closing %s (%s)\n", partition, strerror(errno));
                 return -1;
             }
@@ -494,7 +484,7 @@
 int ParseSha1(const char* str, uint8_t* digest) {
     const char* ps = str;
     uint8_t* pd = digest;
-    for (int i = 0; i < SHA_DIGEST_SIZE * 2; ++i, ++ps) {
+    for (int i = 0; i < SHA_DIGEST_LENGTH * 2; ++i, ++ps) {
         int digit;
         if (*ps >= '0' && *ps <= '9') {
             digit = *ps - '0';
@@ -521,10 +511,10 @@
 // found.
 int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str,
                       int num_patches) {
-    uint8_t patch_sha1[SHA_DIGEST_SIZE];
+    uint8_t patch_sha1[SHA_DIGEST_LENGTH];
     for (int i = 0; i < num_patches; ++i) {
         if (ParseSha1(patch_sha1_str[i], patch_sha1) == 0 &&
-            memcmp(patch_sha1, sha1, SHA_DIGEST_SIZE) == 0) {
+            memcmp(patch_sha1, sha1, SHA_DIGEST_LENGTH) == 0) {
             return i;
         }
     }
@@ -537,7 +527,6 @@
 int applypatch_check(const char* filename, int num_patches,
                      char** const patch_sha1_str) {
     FileContents file;
-    file.data = NULL;
 
     // It's okay to specify no sha1s; the check will pass if the
     // LoadFileContents is successful.  (Useful for reading
@@ -549,9 +538,6 @@
         printf("file \"%s\" doesn't have any of expected "
                "sha1 sums; checking cache\n", filename);
 
-        free(file.data);
-        file.data = NULL;
-
         // If the source file is missing or corrupted, it might be because
         // we were killed in the middle of patching it.  A copy of it
         // should have been made in CACHE_TEMP_SOURCE.  If that file
@@ -565,12 +551,9 @@
 
         if (FindMatchingPatch(file.sha1, patch_sha1_str, num_patches) < 0) {
             printf("cache bits don't match any sha1 for \"%s\"\n", filename);
-            free(file.data);
             return 1;
         }
     }
-
-    free(file.data);
     return 0;
 }
 
@@ -580,11 +563,11 @@
 }
 
 ssize_t FileSink(const unsigned char* data, ssize_t len, void* token) {
-    int fd = *reinterpret_cast<int *>(token);
+    int fd = *static_cast<int*>(token);
     ssize_t done = 0;
     ssize_t wrote;
     while (done < len) {
-        wrote = TEMP_FAILURE_RETRY(write(fd, data+done, len-done));
+        wrote = TEMP_FAILURE_RETRY(ota_write(fd, data+done, len-done));
         if (wrote == -1) {
             printf("error writing %zd bytes: %s\n", (len-done), strerror(errno));
             return done;
@@ -594,19 +577,9 @@
     return done;
 }
 
-typedef struct {
-    unsigned char* buffer;
-    ssize_t size;
-    ssize_t pos;
-} MemorySinkInfo;
-
 ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) {
-    MemorySinkInfo* msi = reinterpret_cast<MemorySinkInfo*>(token);
-    if (msi->size - msi->pos < len) {
-        return -1;
-    }
-    memcpy(msi->buffer + msi->pos, data, len);
-    msi->pos += len;
+    std::string* s = static_cast<std::string*>(token);
+    s->append(reinterpret_cast<const char*>(data), len);
     return len;
 }
 
@@ -670,7 +643,7 @@
         target_filename = source_filename;
     }
 
-    uint8_t target_sha1[SHA_DIGEST_SIZE];
+    uint8_t target_sha1[SHA_DIGEST_LENGTH];
     if (ParseSha1(target_sha1_str, target_sha1) != 0) {
         printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
         return 1;
@@ -678,33 +651,29 @@
 
     FileContents copy_file;
     FileContents source_file;
-    copy_file.data = NULL;
-    source_file.data = NULL;
     const Value* source_patch_value = NULL;
     const Value* copy_patch_value = NULL;
 
     // We try to load the target file into the source_file object.
     if (LoadFileContents(target_filename, &source_file) == 0) {
-        if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) {
+        if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) {
             // The early-exit case:  the patch was already applied, this file
             // has the desired hash, nothing for us to do.
             printf("already %s\n", short_sha1(target_sha1).c_str());
-            free(source_file.data);
             return 0;
         }
     }
 
-    if (source_file.data == NULL ||
+    if (source_file.data.empty() ||
         (target_filename != source_filename &&
          strcmp(target_filename, source_filename) != 0)) {
         // Need to load the source file:  either we failed to load the
         // target file, or we did but it's different from the source file.
-        free(source_file.data);
-        source_file.data = NULL;
+        source_file.data.clear();
         LoadFileContents(source_filename, &source_file);
     }
 
-    if (source_file.data != NULL) {
+    if (!source_file.data.empty()) {
         int to_use = FindMatchingPatch(source_file.sha1, patch_sha1_str, num_patches);
         if (to_use >= 0) {
             source_patch_value = patch_data[to_use];
@@ -712,8 +681,7 @@
     }
 
     if (source_patch_value == NULL) {
-        free(source_file.data);
-        source_file.data = NULL;
+        source_file.data.clear();
         printf("source file is bad; trying copy\n");
 
         if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file) < 0) {
@@ -730,19 +698,14 @@
         if (copy_patch_value == NULL) {
             // fail.
             printf("copy file doesn't match source SHA-1s either\n");
-            free(copy_file.data);
             return 1;
         }
     }
 
-    int result = GenerateTarget(&source_file, source_patch_value,
-                                &copy_file, copy_patch_value,
-                                source_filename, target_filename,
-                                target_sha1, target_size, bonus_data);
-    free(source_file.data);
-    free(copy_file.data);
-
-    return result;
+    return GenerateTarget(&source_file, source_patch_value,
+                          &copy_file, copy_patch_value,
+                          source_filename, target_filename,
+                          target_sha1, target_size, bonus_data);
 }
 
 /*
@@ -756,14 +719,13 @@
                      const char* target_sha1_str, size_t target_size) {
     printf("flash %s: ", target_filename);
 
-    uint8_t target_sha1[SHA_DIGEST_SIZE];
+    uint8_t target_sha1[SHA_DIGEST_LENGTH];
     if (ParseSha1(target_sha1_str, target_sha1) != 0) {
         printf("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
         return 1;
     }
 
     FileContents source_file;
-    source_file.data = NULL;
     std::string target_str(target_filename);
 
     std::vector<std::string> pieces = android::base::Split(target_str, ":");
@@ -777,32 +739,27 @@
     pieces.push_back(target_sha1_str);
     std::string fullname = android::base::Join(pieces, ':');
     if (LoadPartitionContents(fullname.c_str(), &source_file) == 0 &&
-        memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) {
+        memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) == 0) {
         // The early-exit case: the image was already applied, this partition
         // has the desired hash, nothing for us to do.
         printf("already %s\n", short_sha1(target_sha1).c_str());
-        free(source_file.data);
         return 0;
     }
 
     if (LoadFileContents(source_filename, &source_file) == 0) {
-        if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
+        if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) {
             // The source doesn't have desired checksum.
             printf("source \"%s\" doesn't have expected sha1 sum\n", source_filename);
             printf("expected: %s, found: %s\n", short_sha1(target_sha1).c_str(),
                     short_sha1(source_file.sha1).c_str());
-            free(source_file.data);
             return 1;
         }
     }
 
-    if (WriteToPartition(source_file.data, target_size, target_filename) != 0) {
+    if (WriteToPartition(source_file.data.data(), target_size, target_filename) != 0) {
         printf("write of copied data to %s failed\n", target_filename);
-        free(source_file.data);
         return 1;
     }
-
-    free(source_file.data);
     return 0;
 }
 
@@ -812,36 +769,57 @@
                           const Value* copy_patch_value,
                           const char* source_filename,
                           const char* target_filename,
-                          const uint8_t target_sha1[SHA_DIGEST_SIZE],
+                          const uint8_t target_sha1[SHA_DIGEST_LENGTH],
                           size_t target_size,
                           const Value* bonus_data) {
     int retry = 1;
     SHA_CTX ctx;
-    int output;
-    MemorySinkInfo msi;
+    std::string memory_sink_str;
     FileContents* source_to_use;
-    char* outname;
     int made_copy = 0;
 
+    bool target_is_partition = (strncmp(target_filename, "MTD:", 4) == 0 ||
+                                strncmp(target_filename, "EMMC:", 5) == 0);
+    const std::string tmp_target_filename = std::string(target_filename) + ".patch";
+
     // assume that target_filename (eg "/system/app/Foo.apk") is located
     // on the same filesystem as its top-level directory ("/system").
     // We need something that exists for calling statfs().
-    char target_fs[strlen(target_filename)+1];
-    char* slash = strchr(target_filename+1, '/');
-    if (slash != NULL) {
-        int count = slash - target_filename;
-        strncpy(target_fs, target_filename, count);
-        target_fs[count] = '\0';
+    std::string target_fs = target_filename;
+    auto slash_pos = target_fs.find('/', 1);
+    if (slash_pos != std::string::npos) {
+        target_fs.resize(slash_pos);
+    }
+
+    const Value* patch;
+    if (source_patch_value != NULL) {
+        source_to_use = source_file;
+        patch = source_patch_value;
     } else {
-        strcpy(target_fs, target_filename);
+        source_to_use = copy_file;
+        patch = copy_patch_value;
+    }
+    if (patch->type != VAL_BLOB) {
+        printf("patch is not a blob\n");
+        return 1;
+    }
+    char* header = patch->data;
+    ssize_t header_bytes_read = patch->size;
+    bool use_bsdiff = false;
+    if (header_bytes_read >= 8 && memcmp(header, "BSDIFF40", 8) == 0) {
+        use_bsdiff = true;
+    } else if (header_bytes_read >= 8 && memcmp(header, "IMGDIFF2", 8) == 0) {
+        use_bsdiff = false;
+    } else {
+        printf("Unknown patch file format\n");
+        return 1;
     }
 
     do {
         // Is there enough room in the target filesystem to hold the patched
         // file?
 
-        if (strncmp(target_filename, "MTD:", 4) == 0 ||
-            strncmp(target_filename, "EMMC:", 5) == 0) {
+        if (target_is_partition) {
             // If the target is a partition, we're actually going to
             // write the output to /tmp and then copy it to the
             // partition.  statfs() always returns 0 blocks free for
@@ -850,7 +828,7 @@
 
             // We still write the original source to cache, in case
             // the partition write is interrupted.
-            if (MakeFreeSpaceOnCache(source_file->size) < 0) {
+            if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
                 printf("not enough free space on /cache\n");
                 return 1;
             }
@@ -863,7 +841,7 @@
         } else {
             int enough_space = 0;
             if (retry > 0) {
-                size_t free_space = FreeSpaceForFile(target_fs);
+                size_t free_space = FreeSpaceForFile(target_fs.c_str());
                 enough_space =
                     (free_space > (256 << 10)) &&          // 256k (two-block) minimum
                     (free_space > (target_size * 3 / 2));  // 50% margin of error
@@ -891,7 +869,7 @@
                     return 1;
                 }
 
-                if (MakeFreeSpaceOnCache(source_file->size) < 0) {
+                if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
                     printf("not enough free space on /cache\n");
                     return 1;
                 }
@@ -903,84 +881,53 @@
                 made_copy = 1;
                 unlink(source_filename);
 
-                size_t free_space = FreeSpaceForFile(target_fs);
+                size_t free_space = FreeSpaceForFile(target_fs.c_str());
                 printf("(now %zu bytes free for target) ", free_space);
             }
         }
 
-        const Value* patch;
-        if (source_patch_value != NULL) {
-            source_to_use = source_file;
-            patch = source_patch_value;
-        } else {
-            source_to_use = copy_file;
-            patch = copy_patch_value;
-        }
-
-        if (patch->type != VAL_BLOB) {
-            printf("patch is not a blob\n");
-            return 1;
-        }
 
         SinkFn sink = NULL;
         void* token = NULL;
-        output = -1;
-        outname = NULL;
-        if (strncmp(target_filename, "MTD:", 4) == 0 ||
-            strncmp(target_filename, "EMMC:", 5) == 0) {
+        int output_fd = -1;
+        if (target_is_partition) {
             // We store the decoded output in memory.
-            msi.buffer = reinterpret_cast<unsigned char*>(malloc(target_size));
-            if (msi.buffer == NULL) {
-                printf("failed to alloc %zu bytes for output\n", target_size);
-                return 1;
-            }
-            msi.pos = 0;
-            msi.size = target_size;
             sink = MemorySink;
-            token = &msi;
+            token = &memory_sink_str;
         } else {
             // We write the decoded output to "<tgt-file>.patch".
-            outname = reinterpret_cast<char*>(malloc(strlen(target_filename) + 10));
-            strcpy(outname, target_filename);
-            strcat(outname, ".patch");
-
-            output = open(outname, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR);
-            if (output < 0) {
-                printf("failed to open output file %s: %s\n",
-                       outname, strerror(errno));
+            output_fd = ota_open(tmp_target_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC,
+                                 S_IRUSR | S_IWUSR);
+            if (output_fd < 0) {
+                printf("failed to open output file %s: %s\n", tmp_target_filename.c_str(),
+                       strerror(errno));
                 return 1;
             }
             sink = FileSink;
-            token = &output;
+            token = &output_fd;
         }
 
-        char* header = patch->data;
-        ssize_t header_bytes_read = patch->size;
 
-        SHA_init(&ctx);
+        SHA1_Init(&ctx);
 
         int result;
-
-        if (header_bytes_read >= 8 &&
-            memcmp(header, "BSDIFF40", 8) == 0) {
-            result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size,
+        if (use_bsdiff) {
+            result = ApplyBSDiffPatch(source_to_use->data.data(), source_to_use->data.size(),
                                       patch, 0, sink, token, &ctx);
-        } else if (header_bytes_read >= 8 &&
-                   memcmp(header, "IMGDIFF2", 8) == 0) {
-            result = ApplyImagePatch(source_to_use->data, source_to_use->size,
-                                     patch, sink, token, &ctx, bonus_data);
         } else {
-            printf("Unknown patch file format\n");
-            return 1;
+            result = ApplyImagePatch(source_to_use->data.data(), source_to_use->data.size(),
+                                     patch, sink, token, &ctx, bonus_data);
         }
 
-        if (output >= 0) {
-            if (fsync(output) != 0) {
-                printf("failed to fsync file \"%s\" (%s)\n", outname, strerror(errno));
+        if (!target_is_partition) {
+            if (ota_fsync(output_fd) != 0) {
+                printf("failed to fsync file \"%s\" (%s)\n", tmp_target_filename.c_str(),
+                       strerror(errno));
                 result = 1;
             }
-            if (close(output) != 0) {
-                printf("failed to close file \"%s\" (%s)\n", outname, strerror(errno));
+            if (ota_close(output_fd) != 0) {
+                printf("failed to close file \"%s\" (%s)\n", tmp_target_filename.c_str(),
+                       strerror(errno));
                 result = 1;
             }
         }
@@ -992,8 +939,8 @@
             } else {
                 printf("applying patch failed; retrying\n");
             }
-            if (outname != NULL) {
-                unlink(outname);
+            if (!target_is_partition) {
+                unlink(tmp_target_filename.c_str());
             }
         } else {
             // succeeded; no need to retry
@@ -1001,35 +948,36 @@
         }
     } while (retry-- > 0);
 
-    const uint8_t* current_target_sha1 = SHA_final(&ctx);
-    if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
+    uint8_t current_target_sha1[SHA_DIGEST_LENGTH];
+    SHA1_Final(current_target_sha1, &ctx);
+    if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_LENGTH) != 0) {
         printf("patch did not produce expected sha1\n");
         return 1;
     } else {
         printf("now %s\n", short_sha1(target_sha1).c_str());
     }
 
-    if (output < 0) {
+    if (target_is_partition) {
         // Copy the temp file to the partition.
-        if (WriteToPartition(msi.buffer, msi.pos, target_filename) != 0) {
+        if (WriteToPartition(reinterpret_cast<const unsigned char*>(memory_sink_str.c_str()),
+                             memory_sink_str.size(), target_filename) != 0) {
             printf("write of patched data to %s failed\n", target_filename);
             return 1;
         }
-        free(msi.buffer);
     } else {
         // Give the .patch file the same owner, group, and mode of the
         // original source file.
-        if (chmod(outname, source_to_use->st.st_mode) != 0) {
-            printf("chmod of \"%s\" failed: %s\n", outname, strerror(errno));
+        if (chmod(tmp_target_filename.c_str(), source_to_use->st.st_mode) != 0) {
+            printf("chmod of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno));
             return 1;
         }
-        if (chown(outname, source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) {
-            printf("chown of \"%s\" failed: %s\n", outname, strerror(errno));
+        if (chown(tmp_target_filename.c_str(), source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) {
+            printf("chown of \"%s\" failed: %s\n", tmp_target_filename.c_str(), strerror(errno));
             return 1;
         }
 
         // Finally, rename the .patch file to replace the target file.
-        if (rename(outname, target_filename) != 0) {
+        if (rename(tmp_target_filename.c_str(), target_filename) != 0) {
             printf("rename of .patch to \"%s\" failed: %s\n", target_filename, strerror(errno));
             return 1;
         }
diff --git a/applypatch/applypatch.h b/applypatch/applypatch.h
index 415bc1b..9ee39d2 100644
--- a/applypatch/applypatch.h
+++ b/applypatch/applypatch.h
@@ -17,21 +17,19 @@
 #ifndef _APPLYPATCH_H
 #define _APPLYPATCH_H
 
+#include <stdint.h>
 #include <sys/stat.h>
-#include "mincrypt/sha.h"
+
+#include <vector>
+
+#include "openssl/sha.h"
 #include "edify/expr.h"
 
-typedef struct _Patch {
-  uint8_t sha1[SHA_DIGEST_SIZE];
-  const char* patch_filename;
-} Patch;
-
-typedef struct _FileContents {
-  uint8_t sha1[SHA_DIGEST_SIZE];
-  unsigned char* data;
-  ssize_t size;
+struct FileContents {
+  uint8_t sha1[SHA_DIGEST_LENGTH];
+  std::vector<unsigned char> data;
   struct stat st;
-} FileContents;
+};
 
 // When there isn't enough room on the target filesystem to hold the
 // patched version of the file, we copy the original here and delete
@@ -68,22 +66,22 @@
 int FindMatchingPatch(uint8_t* sha1, char* const * const patch_sha1_str,
                       int num_patches);
 
-// bsdiff.c
+// bsdiff.cpp
 void ShowBSDiffLicense();
 int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
                      const Value* patch, ssize_t patch_offset,
                      SinkFn sink, void* token, SHA_CTX* ctx);
 int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
                         const Value* patch, ssize_t patch_offset,
-                        unsigned char** new_data, ssize_t* new_size);
+                        std::vector<unsigned char>* new_data);
 
-// imgpatch.c
+// imgpatch.cpp
 int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
                     const Value* patch,
                     SinkFn sink, void* token, SHA_CTX* ctx,
                     const Value* bonus_data);
 
-// freecache.c
+// freecache.cpp
 int MakeFreeSpaceOnCache(size_t bytes_needed);
 
 #endif
diff --git a/applypatch/bsdiff.cpp b/applypatch/bsdiff.cpp
index 55dbe5c..cca1b32 100644
--- a/applypatch/bsdiff.cpp
+++ b/applypatch/bsdiff.cpp
@@ -224,7 +224,6 @@
 int bsdiff(u_char* old, off_t oldsize, off_t** IP, u_char* newdata, off_t newsize,
            const char* patch_filename)
 {
-	int fd;
 	off_t *I;
 	off_t scan,pos,len;
 	off_t lastscan,lastpos,lastoffset;
diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp
index 9d201b4..1fc1455 100644
--- a/applypatch/bspatch.cpp
+++ b/applypatch/bspatch.cpp
@@ -22,14 +22,14 @@
 
 #include <stdio.h>
 #include <sys/stat.h>
+#include <sys/types.h>
 #include <errno.h>
-#include <malloc.h>
 #include <unistd.h>
 #include <string.h>
 
 #include <bzlib.h>
 
-#include "mincrypt/sha.h"
+#include "openssl/sha.h"
 #include "applypatch.h"
 
 void ShowBSDiffLicense() {
@@ -102,26 +102,22 @@
                      const Value* patch, ssize_t patch_offset,
                      SinkFn sink, void* token, SHA_CTX* ctx) {
 
-    unsigned char* new_data;
-    ssize_t new_size;
-    if (ApplyBSDiffPatchMem(old_data, old_size, patch, patch_offset,
-                            &new_data, &new_size) != 0) {
+    std::vector<unsigned char> new_data;
+    if (ApplyBSDiffPatchMem(old_data, old_size, patch, patch_offset, &new_data) != 0) {
         return -1;
     }
 
-    if (sink(new_data, new_size, token) < new_size) {
+    if (sink(new_data.data(), new_data.size(), token) < static_cast<ssize_t>(new_data.size())) {
         printf("short write of output: %d (%s)\n", errno, strerror(errno));
         return 1;
     }
-    if (ctx) SHA_update(ctx, new_data, new_size);
-    free(new_data);
-
+    if (ctx) SHA1_Update(ctx, new_data.data(), new_data.size());
     return 0;
 }
 
 int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
                         const Value* patch, ssize_t patch_offset,
-                        unsigned char** new_data, ssize_t* new_size) {
+                        std::vector<unsigned char>* new_data) {
     // Patch data format:
     //   0       8       "BSDIFF40"
     //   8       8       X
@@ -140,12 +136,12 @@
         return 1;
     }
 
-    ssize_t ctrl_len, data_len;
+    ssize_t ctrl_len, data_len, new_size;
     ctrl_len = offtin(header+8);
     data_len = offtin(header+16);
-    *new_size = offtin(header+24);
+    new_size = offtin(header+24);
 
-    if (ctrl_len < 0 || data_len < 0 || *new_size < 0) {
+    if (ctrl_len < 0 || data_len < 0 || new_size < 0) {
         printf("corrupt patch file header (data lengths)\n");
         return 1;
     }
@@ -182,18 +178,13 @@
         printf("failed to bzinit extra stream (%d)\n", bzerr);
     }
 
-    *new_data = reinterpret_cast<unsigned char*>(malloc(*new_size));
-    if (*new_data == NULL) {
-        printf("failed to allocate %zd bytes of memory for output file\n", *new_size);
-        return 1;
-    }
+    new_data->resize(new_size);
 
     off_t oldpos = 0, newpos = 0;
     off_t ctrl[3];
-    off_t len_read;
     int i;
     unsigned char buf[24];
-    while (newpos < *new_size) {
+    while (newpos < new_size) {
         // Read control data
         if (FillBuffer(buf, 24, &cstream) != 0) {
             printf("error while reading control stream\n");
@@ -209,13 +200,13 @@
         }
 
         // Sanity check
-        if (newpos + ctrl[0] > *new_size) {
+        if (newpos + ctrl[0] > new_size) {
             printf("corrupt patch (new file overrun)\n");
             return 1;
         }
 
         // Read diff string
-        if (FillBuffer(*new_data + newpos, ctrl[0], &dstream) != 0) {
+        if (FillBuffer(new_data->data() + newpos, ctrl[0], &dstream) != 0) {
             printf("error while reading diff stream\n");
             return 1;
         }
@@ -232,13 +223,13 @@
         oldpos += ctrl[0];
 
         // Sanity check
-        if (newpos + ctrl[1] > *new_size) {
+        if (newpos + ctrl[1] > new_size) {
             printf("corrupt patch (new file overrun)\n");
             return 1;
         }
 
         // Read extra string
-        if (FillBuffer(*new_data + newpos, ctrl[1], &estream) != 0) {
+        if (FillBuffer(new_data->data() + newpos, ctrl[1], &estream) != 0) {
             printf("error while reading extra stream\n");
             return 1;
         }
diff --git a/applypatch/freecache.cpp b/applypatch/freecache.cpp
index 2eb2f55..c84f427 100644
--- a/applypatch/freecache.cpp
+++ b/applypatch/freecache.cpp
@@ -25,119 +25,90 @@
 #include <dirent.h>
 #include <ctype.h>
 
+#include <memory>
+#include <set>
+#include <string>
+
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+
 #include "applypatch.h"
 
-static int EliminateOpenFiles(char** files, int file_count) {
-  DIR* d;
-  struct dirent* de;
-  d = opendir("/proc");
-  if (d == NULL) {
+static int EliminateOpenFiles(std::set<std::string>* files) {
+  std::unique_ptr<DIR, decltype(&closedir)> d(opendir("/proc"), closedir);
+  if (!d) {
     printf("error opening /proc: %s\n", strerror(errno));
     return -1;
   }
-  while ((de = readdir(d)) != 0) {
-    int i;
-    for (i = 0; de->d_name[i] != '\0' && isdigit(de->d_name[i]); ++i);
-    if (de->d_name[i]) continue;
+  struct dirent* de;
+  while ((de = readdir(d.get())) != 0) {
+    unsigned int pid;
+    if (!android::base::ParseUint(de->d_name, &pid)) {
+        continue;
+    }
+    std::string path = android::base::StringPrintf("/proc/%s/fd/", de->d_name);
 
-    // de->d_name[i] is numeric
-
-    char path[FILENAME_MAX];
-    strcpy(path, "/proc/");
-    strcat(path, de->d_name);
-    strcat(path, "/fd/");
-
-    DIR* fdd;
     struct dirent* fdde;
-    fdd = opendir(path);
-    if (fdd == NULL) {
-      printf("error opening %s: %s\n", path, strerror(errno));
+    std::unique_ptr<DIR, decltype(&closedir)> fdd(opendir(path.c_str()), closedir);
+    if (!fdd) {
+      printf("error opening %s: %s\n", path.c_str(), strerror(errno));
       continue;
     }
-    while ((fdde = readdir(fdd)) != 0) {
-      char fd_path[FILENAME_MAX];
+    while ((fdde = readdir(fdd.get())) != 0) {
+      std::string fd_path = path + fdde->d_name;
       char link[FILENAME_MAX];
-      strcpy(fd_path, path);
-      strcat(fd_path, fdde->d_name);
 
-      int count;
-      count = readlink(fd_path, link, sizeof(link)-1);
+      int count = readlink(fd_path.c_str(), link, sizeof(link)-1);
       if (count >= 0) {
         link[count] = '\0';
-
-        // This is inefficient, but it should only matter if there are
-        // lots of files in /cache, and lots of them are open (neither
-        // of which should be true, especially in recovery).
         if (strncmp(link, "/cache/", 7) == 0) {
-          int j;
-          for (j = 0; j < file_count; ++j) {
-            if (files[j] && strcmp(files[j], link) == 0) {
-              printf("%s is open by %s\n", link, de->d_name);
-              free(files[j]);
-              files[j] = NULL;
-            }
+          if (files->erase(link) > 0) {
+            printf("%s is open by %s\n", link, de->d_name);
           }
         }
       }
     }
-    closedir(fdd);
   }
-  closedir(d);
-
   return 0;
 }
 
-int FindExpendableFiles(char*** names, int* entries) {
-  DIR* d;
-  struct dirent* de;
-  int size = 32;
-  *entries = 0;
-  *names = reinterpret_cast<char**>(malloc(size * sizeof(char*)));
-
-  char path[FILENAME_MAX];
-
+static std::set<std::string> FindExpendableFiles() {
+  std::set<std::string> files;
   // We're allowed to delete unopened regular files in any of these
   // directories.
   const char* dirs[2] = {"/cache", "/cache/recovery/otatest"};
 
   for (size_t i = 0; i < sizeof(dirs)/sizeof(dirs[0]); ++i) {
-    d = opendir(dirs[i]);
-    if (d == NULL) {
+    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dirs[i]), closedir);
+    if (!d) {
       printf("error opening %s: %s\n", dirs[i], strerror(errno));
       continue;
     }
 
     // Look for regular files in the directory (not in any subdirectories).
-    while ((de = readdir(d)) != 0) {
-      strcpy(path, dirs[i]);
-      strcat(path, "/");
-      strcat(path, de->d_name);
+    struct dirent* de;
+    while ((de = readdir(d.get())) != 0) {
+      std::string path = std::string(dirs[i]) + "/" + de->d_name;
 
       // We can't delete CACHE_TEMP_SOURCE; if it's there we might have
       // restarted during installation and could be depending on it to
       // be there.
-      if (strcmp(path, CACHE_TEMP_SOURCE) == 0) continue;
+      if (path == CACHE_TEMP_SOURCE) {
+        continue;
+      }
 
       struct stat st;
-      if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) {
-        if (*entries >= size) {
-          size *= 2;
-          *names = reinterpret_cast<char**>(realloc(*names, size * sizeof(char*)));
-        }
-        (*names)[(*entries)++] = strdup(path);
+      if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
+        files.insert(path);
       }
     }
-
-    closedir(d);
   }
 
-  printf("%d regular files in deletable directories\n", *entries);
-
-  if (EliminateOpenFiles(*names, *entries) < 0) {
-    return -1;
+  printf("%zu regular files in deletable directories\n", files.size());
+  if (EliminateOpenFiles(&files) < 0) {
+    return std::set<std::string>();
   }
-
-  return 0;
+  return files;
 }
 
 int MakeFreeSpaceOnCache(size_t bytes_needed) {
@@ -147,15 +118,8 @@
   if (free_now >= bytes_needed) {
     return 0;
   }
-
-  char** names;
-  int entries;
-
-  if (FindExpendableFiles(&names, &entries) < 0) {
-    return -1;
-  }
-
-  if (entries == 0) {
+  std::set<std::string> files = FindExpendableFiles();
+  if (files.empty()) {
     // nothing we can delete to free up space!
     printf("no files can be deleted to free space on /cache\n");
     return -1;
@@ -167,20 +131,13 @@
   //
   // Instead, we'll be dumb.
 
-  int i;
-  for (i = 0; i < entries && free_now < bytes_needed; ++i) {
-    if (names[i]) {
-      unlink(names[i]);
-      free_now = FreeSpaceForFile("/cache");
-      printf("deleted %s; now %zu bytes free\n", names[i], free_now);
-      free(names[i]);
+  for (const auto& file : files) {
+    unlink(file.c_str());
+    free_now = FreeSpaceForFile("/cache");
+    printf("deleted %s; now %zu bytes free\n", file.c_str(), free_now);
+    if (free_now < bytes_needed) {
+        break;
     }
   }
-
-  for (; i < entries; ++i) {
-    free(names[i]);
-  }
-  free(names);
-
   return (free_now >= bytes_needed) ? 0 : -1;
 }
diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp
index f22502e..2aa4a68 100644
--- a/applypatch/imgdiff.cpp
+++ b/applypatch/imgdiff.cpp
@@ -407,7 +407,6 @@
   while (pos < sz) {
     unsigned char* p = img+pos;
 
-    bool processed_deflate = false;
     if (sz - pos >= 4 &&
         p[0] == 0x1f && p[1] == 0x8b &&
         p[2] == 0x08 &&    // deflate compression
@@ -461,28 +460,27 @@
         strm.next_out = curr->data + curr->len;
         ret = inflate(&strm, Z_NO_FLUSH);
         if (ret < 0) {
-          if (!processed_deflate) {
-            // This is the first chunk, assume that it's just a spurious
-            // gzip header instead of a real one.
-            break;
-          }
-          printf("Error: inflate failed [%s] at file offset [%zu]\n"
-                 "imgdiff only supports gzip kernel compression,"
-                 " did you try CONFIG_KERNEL_LZO?\n",
+          printf("Warning: inflate failed [%s] at offset [%zu],"
+                 " treating as a normal chunk\n",
                  strm.msg, chunk_offset);
-          free(img);
-          return NULL;
+          break;
         }
         curr->len = allocated - strm.avail_out;
         if (strm.avail_out == 0) {
           allocated *= 2;
           curr->data = reinterpret_cast<unsigned char*>(realloc(curr->data, allocated));
         }
-        processed_deflate = true;
       } while (ret != Z_STREAM_END);
 
       curr->deflate_len = sz - strm.avail_in - pos;
       inflateEnd(&strm);
+
+      if (ret < 0) {
+        free(curr->data);
+        *num_chunks -= 2;
+        continue;
+      }
+
       pos += curr->deflate_len;
       p += curr->deflate_len;
       ++curr;
@@ -598,7 +596,6 @@
     return -1;
   }
 
-  size_t p = 0;
   unsigned char* out = reinterpret_cast<unsigned char*>(malloc(BUFFER_SIZE));
 
   // We only check two combinations of encoder parameters:  level 6
@@ -844,7 +841,6 @@
   }
 
   if (argc != 4) {
-    usage:
     printf("usage: %s [-z] [-b <bonus-file>] <src-img> <tgt-img> <patch-file>\n",
             argv[0]);
     return 2;
diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp
index 26888f8..0ab995b 100644
--- a/applypatch/imgpatch.cpp
+++ b/applypatch/imgpatch.cpp
@@ -21,23 +21,33 @@
 #include <sys/cdefs.h>
 #include <sys/stat.h>
 #include <errno.h>
-#include <malloc.h>
 #include <unistd.h>
 #include <string.h>
 
+#include <vector>
+
 #include "zlib.h"
-#include "mincrypt/sha.h"
+#include "openssl/sha.h"
 #include "applypatch.h"
 #include "imgdiff.h"
 #include "utils.h"
 
+int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
+                    const unsigned char* patch_data, ssize_t patch_size,
+                    SinkFn sink, void* token) {
+  Value patch = {VAL_BLOB, patch_size,
+      reinterpret_cast<char*>(const_cast<unsigned char*>(patch_data))};
+  return ApplyImagePatch(
+      old_data, old_size, &patch, sink, token, nullptr, nullptr);
+}
+
 /*
  * Apply the patch given in 'patch_filename' to the source data given
  * by (old_data, old_size).  Write the patched output to the 'output'
  * file, and update the SHA context with the output data as well.
  * Return 0 on success.
  */
-int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size __unused,
+int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
                     const Value* patch,
                     SinkFn sink, void* token, SHA_CTX* ctx,
                     const Value* bonus_data) {
@@ -80,6 +90,10 @@
             size_t src_len = Read8(normal_header+8);
             size_t patch_offset = Read8(normal_header+16);
 
+            if (src_start + src_len > static_cast<size_t>(old_size)) {
+                printf("source data too short\n");
+                return -1;
+            }
             ApplyBSDiffPatch(old_data + src_start, src_len,
                              patch, patch_offset, sink, token, ctx);
         } else if (type == CHUNK_RAW) {
@@ -96,7 +110,7 @@
                 printf("failed to read chunk %d raw data\n", i);
                 return -1;
             }
-            if (ctx) SHA_update(ctx, patch->data + pos, data_len);
+            if (ctx) SHA1_Update(ctx, patch->data + pos, data_len);
             if (sink((unsigned char*)patch->data + pos,
                      data_len, token) != data_len) {
                 printf("failed to write chunk %d raw data\n", i);
@@ -123,6 +137,11 @@
             int memLevel = Read4(deflate_header+52);
             int strategy = Read4(deflate_header+56);
 
+            if (src_start + src_len > static_cast<size_t>(old_size)) {
+                printf("source data too short\n");
+                return -1;
+            }
+
             // Decompress the source data; the chunk header tells us exactly
             // how big we expect it to be when decompressed.
 
@@ -132,13 +151,7 @@
             // must be appended from the bonus_data value.
             size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->size : 0;
 
-            unsigned char* expanded_source = reinterpret_cast<unsigned char*>(malloc(expanded_len));
-            if (expanded_source == NULL) {
-                printf("failed to allocate %zu bytes for expanded_source\n",
-                       expanded_len);
-                return -1;
-            }
-
+            std::vector<unsigned char> expanded_source(expanded_len);
             z_stream strm;
             strm.zalloc = Z_NULL;
             strm.zfree = Z_NULL;
@@ -146,7 +159,7 @@
             strm.avail_in = src_len;
             strm.next_in = (unsigned char*)(old_data + src_start);
             strm.avail_out = expanded_len;
-            strm.next_out = expanded_source;
+            strm.next_out = expanded_source.data();
 
             int ret;
             ret = inflateInit2(&strm, -15);
@@ -171,18 +184,21 @@
             inflateEnd(&strm);
 
             if (bonus_size) {
-                memcpy(expanded_source + (expanded_len - bonus_size),
+                memcpy(expanded_source.data() + (expanded_len - bonus_size),
                        bonus_data->data, bonus_size);
             }
 
             // Next, apply the bsdiff patch (in memory) to the uncompressed
             // data.
-            unsigned char* uncompressed_target_data;
-            ssize_t uncompressed_target_size;
-            if (ApplyBSDiffPatchMem(expanded_source, expanded_len,
+            std::vector<unsigned char> uncompressed_target_data;
+            if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len,
                                     patch, patch_offset,
-                                    &uncompressed_target_data,
-                                    &uncompressed_target_size) != 0) {
+                                    &uncompressed_target_data) != 0) {
+                return -1;
+            }
+            if (uncompressed_target_data.size() != target_len) {
+                printf("expected target len to be %zu, but it's %zu\n",
+                       target_len, uncompressed_target_data.size());
                 return -1;
             }
 
@@ -190,40 +206,36 @@
 
             // we're done with the expanded_source data buffer, so we'll
             // reuse that memory to receive the output of deflate.
-            unsigned char* temp_data = expanded_source;
-            ssize_t temp_size = expanded_len;
-            if (temp_size < 32768) {
-                // ... unless the buffer is too small, in which case we'll
-                // allocate a fresh one.
-                free(temp_data);
-                temp_data = reinterpret_cast<unsigned char*>(malloc(32768));
-                temp_size = 32768;
+            if (expanded_source.size() < 32768U) {
+                expanded_source.resize(32768U);
             }
+            std::vector<unsigned char>& temp_data = expanded_source;
 
             // now the deflate stream
             strm.zalloc = Z_NULL;
             strm.zfree = Z_NULL;
             strm.opaque = Z_NULL;
-            strm.avail_in = uncompressed_target_size;
-            strm.next_in = uncompressed_target_data;
+            strm.avail_in = uncompressed_target_data.size();
+            strm.next_in = uncompressed_target_data.data();
             ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy);
+            if (ret != Z_OK) {
+                printf("failed to init uncompressed data deflation: %d\n", ret);
+                return -1;
+            }
             do {
-                strm.avail_out = temp_size;
-                strm.next_out = temp_data;
+                strm.avail_out = temp_data.size();
+                strm.next_out = temp_data.data();
                 ret = deflate(&strm, Z_FINISH);
-                ssize_t have = temp_size - strm.avail_out;
+                ssize_t have = temp_data.size() - strm.avail_out;
 
-                if (sink(temp_data, have, token) != have) {
+                if (sink(temp_data.data(), have, token) != have) {
                     printf("failed to write %ld compressed bytes to output\n",
                            (long)have);
                     return -1;
                 }
-                if (ctx) SHA_update(ctx, temp_data, have);
+                if (ctx) SHA1_Update(ctx, temp_data.data(), have);
             } while (ret != Z_STREAM_END);
             deflateEnd(&strm);
-
-            free(temp_data);
-            free(uncompressed_target_data);
         } else {
             printf("patch chunk %d is unknown type %d\n", i, type);
             return -1;
diff --git a/applypatch/include/applypatch/imgpatch.h b/applypatch/include/applypatch/imgpatch.h
new file mode 100644
index 0000000..64d9aa9
--- /dev/null
+++ b/applypatch/include/applypatch/imgpatch.h
@@ -0,0 +1,26 @@
+/*
+ * 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 _IMGPATCH_H
+#define _IMGPATCH_H
+
+typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*);
+
+int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
+                    const unsigned char* patch_data, ssize_t patch_size,
+                    SinkFn sink, void* token);
+
+#endif  //_IMGPATCH_H
diff --git a/applypatch/libimgpatch.pc b/applypatch/libimgpatch.pc
new file mode 100644
index 0000000..e500293
--- /dev/null
+++ b/applypatch/libimgpatch.pc
@@ -0,0 +1,6 @@
+# This file is for libimgpatch in Chrome OS.
+
+Name: libimgpatch
+Description: Apply imgdiff patch
+Version: 0.0.1
+Libs: -limgpatch -lbz2 -lz
diff --git a/applypatch/main.cpp b/applypatch/main.cpp
index 966d8b9..9013760 100644
--- a/applypatch/main.cpp
+++ b/applypatch/main.cpp
@@ -19,9 +19,12 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <memory>
+#include <vector>
+
 #include "applypatch.h"
 #include "edify/expr.h"
-#include "mincrypt/sha.h"
+#include "openssl/sha.h"
 
 static int CheckMode(int argc, char** argv) {
     if (argc < 3) {
@@ -43,59 +46,34 @@
     return CacheSizeCheck(bytes);
 }
 
-// Parse arguments (which should be of the form "<sha1>" or
-// "<sha1>:<filename>" into the new parallel arrays *sha1s and
-// *patches (loading file contents into the patches).  Returns true on
+// Parse arguments (which should be of the form "<sha1>:<filename>"
+// into the new parallel arrays *sha1s and *files.Returns true on
 // success.
-static bool ParsePatchArgs(int argc, char** argv, char*** sha1s,
-                           Value*** patches, int* num_patches) {
-    *num_patches = argc;
-    *sha1s = reinterpret_cast<char**>(malloc(*num_patches * sizeof(char*)));
-    *patches = reinterpret_cast<Value**>(malloc(*num_patches * sizeof(Value*)));
-    memset(*patches, 0, *num_patches * sizeof(Value*));
+static bool ParsePatchArgs(int argc, char** argv, std::vector<char*>* sha1s,
+                           std::vector<FileContents>* files) {
+    uint8_t digest[SHA_DIGEST_LENGTH];
 
-    uint8_t digest[SHA_DIGEST_SIZE];
-
-    for (int i = 0; i < *num_patches; ++i) {
+    for (int i = 0; i < argc; ++i) {
         char* colon = strchr(argv[i], ':');
-        if (colon != NULL) {
-            *colon = '\0';
-            ++colon;
+        if (colon == nullptr) {
+            printf("no ':' in patch argument \"%s\"\n", argv[i]);
+            return false;
         }
-
+        *colon = '\0';
+        ++colon;
         if (ParseSha1(argv[i], digest) != 0) {
             printf("failed to parse sha1 \"%s\"\n", argv[i]);
             return false;
         }
 
-        (*sha1s)[i] = argv[i];
-        if (colon == NULL) {
-            (*patches)[i] = NULL;
-        } else {
-            FileContents fc;
-            if (LoadFileContents(colon, &fc) != 0) {
-                goto abort;
-            }
-            (*patches)[i] = reinterpret_cast<Value*>(malloc(sizeof(Value)));
-            (*patches)[i]->type = VAL_BLOB;
-            (*patches)[i]->size = fc.size;
-            (*patches)[i]->data = reinterpret_cast<char*>(fc.data);
+        sha1s->push_back(argv[i]);
+        FileContents fc;
+        if (LoadFileContents(colon, &fc) != 0) {
+            return false;
         }
+        files->push_back(std::move(fc));
     }
-
     return true;
-
-  abort:
-    for (int i = 0; i < *num_patches; ++i) {
-        Value* p = (*patches)[i];
-        if (p != NULL) {
-            free(p->data);
-            free(p);
-        }
-    }
-    free(*sha1s);
-    free(*patches);
-    return false;
 }
 
 static int FlashMode(const char* src_filename, const char* tgt_filename,
@@ -104,17 +82,19 @@
 }
 
 static int PatchMode(int argc, char** argv) {
-    Value* bonus = NULL;
+    FileContents bonusFc;
+    Value bonusValue;
+    Value* bonus = nullptr;
+
     if (argc >= 3 && strcmp(argv[1], "-b") == 0) {
-        FileContents fc;
-        if (LoadFileContents(argv[2], &fc) != 0) {
+        if (LoadFileContents(argv[2], &bonusFc) != 0) {
             printf("failed to load bonus file %s\n", argv[2]);
             return 1;
         }
-        bonus = reinterpret_cast<Value*>(malloc(sizeof(Value)));
+        bonus = &bonusValue;
         bonus->type = VAL_BLOB;
-        bonus->size = fc.size;
-        bonus->data = (char*)fc.data;
+        bonus->size = bonusFc.data.size();
+        bonus->data = reinterpret_cast<char*>(bonusFc.data.data());
         argc -= 2;
         argv += 2;
     }
@@ -132,41 +112,29 @@
 
     // If no <src-sha1>:<patch> is provided, it is in flash mode.
     if (argc == 5) {
-        if (bonus != NULL) {
+        if (bonus != nullptr) {
             printf("bonus file not supported in flash mode\n");
             return 1;
         }
         return FlashMode(argv[1], argv[2], argv[3], target_size);
     }
-
-
-    char** sha1s;
-    Value** patches;
-    int num_patches;
-    if (!ParsePatchArgs(argc-5, argv+5, &sha1s, &patches, &num_patches)) {
+    std::vector<char*> sha1s;
+    std::vector<FileContents> files;
+    if (!ParsePatchArgs(argc-5, argv+5, &sha1s, &files)) {
         printf("failed to parse patch args\n");
         return 1;
     }
-
-    int result = applypatch(argv[1], argv[2], argv[3], target_size,
-                            num_patches, sha1s, patches, bonus);
-
-    int i;
-    for (i = 0; i < num_patches; ++i) {
-        Value* p = patches[i];
-        if (p != NULL) {
-            free(p->data);
-            free(p);
-        }
+    std::vector<Value> patches(files.size());
+    std::vector<Value*> patch_ptrs(files.size());
+    for (size_t i = 0; i < files.size(); ++i) {
+        patches[i].type = VAL_BLOB;
+        patches[i].size = files[i].data.size();
+        patches[i].data = reinterpret_cast<char*>(files[i].data.data());
+        patch_ptrs[i] = &patches[i];
     }
-    if (bonus) {
-        free(bonus->data);
-        free(bonus);
-    }
-    free(sha1s);
-    free(patches);
-
-    return result;
+    return applypatch(argv[1], argv[2], argv[3], target_size,
+                      patch_ptrs.size(), sha1s.data(),
+                      patch_ptrs.data(), bonus);
 }
 
 // This program applies binary patches to files in a way that is safe
diff --git a/bootloader.cpp b/bootloader.cpp
index 600d238..d80c5e7 100644
--- a/bootloader.cpp
+++ b/bootloader.cpp
@@ -14,28 +14,33 @@
  * limitations under the License.
  */
 
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
 #include <fs_mgr.h>
+
 #include "bootloader.h"
 #include "common.h"
 #include "mtdutils/mtdutils.h"
 #include "roots.h"
+#include "unique_fd.h"
 
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
+static int get_bootloader_message_mtd(bootloader_message* out, const Volume* v);
+static int set_bootloader_message_mtd(const bootloader_message* in, const Volume* v);
+static int get_bootloader_message_block(bootloader_message* out, const Volume* v);
+static int set_bootloader_message_block(const bootloader_message* in, const Volume* v);
 
-static int get_bootloader_message_mtd(struct bootloader_message *out, const Volume* v);
-static int set_bootloader_message_mtd(const struct bootloader_message *in, const Volume* v);
-static int get_bootloader_message_block(struct bootloader_message *out, const Volume* v);
-static int set_bootloader_message_block(const struct bootloader_message *in, const Volume* v);
-
-int get_bootloader_message(struct bootloader_message *out) {
+int get_bootloader_message(bootloader_message* out) {
     Volume* v = volume_for_path("/misc");
-    if (v == NULL) {
-      LOGE("Cannot load volume /misc!\n");
-      return -1;
+    if (v == nullptr) {
+        LOGE("Cannot load volume /misc!\n");
+        return -1;
     }
     if (strcmp(v->fs_type, "mtd") == 0) {
         return get_bootloader_message_mtd(out, v);
@@ -46,11 +51,11 @@
     return -1;
 }
 
-int set_bootloader_message(const struct bootloader_message *in) {
+int set_bootloader_message(const bootloader_message* in) {
     Volume* v = volume_for_path("/misc");
-    if (v == NULL) {
-      LOGE("Cannot load volume /misc!\n");
-      return -1;
+    if (v == nullptr) {
+        LOGE("Cannot load volume /misc!\n");
+        return -1;
     }
     if (strcmp(v->fs_type, "mtd") == 0) {
         return set_bootloader_message_mtd(in, v);
@@ -68,69 +73,69 @@
 static const int MISC_PAGES = 3;         // number of pages to save
 static const int MISC_COMMAND_PAGE = 1;  // bootloader command is this page
 
-static int get_bootloader_message_mtd(struct bootloader_message *out,
+static int get_bootloader_message_mtd(bootloader_message* out,
                                       const Volume* v) {
     size_t write_size;
     mtd_scan_partitions();
-    const MtdPartition *part = mtd_find_partition_by_name(v->blk_device);
-    if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
-        LOGE("Can't find %s\n", v->blk_device);
+    const MtdPartition* part = mtd_find_partition_by_name(v->blk_device);
+    if (part == nullptr || mtd_partition_info(part, nullptr, nullptr, &write_size)) {
+        LOGE("failed to find \"%s\"\n", v->blk_device);
         return -1;
     }
 
-    MtdReadContext *read = mtd_read_partition(part);
-    if (read == NULL) {
-        LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno));
+    MtdReadContext* read = mtd_read_partition(part);
+    if (read == nullptr) {
+        LOGE("failed to open \"%s\": %s\n", v->blk_device, strerror(errno));
         return -1;
     }
 
     const ssize_t size = write_size * MISC_PAGES;
     char data[size];
     ssize_t r = mtd_read_data(read, data, size);
-    if (r != size) LOGE("Can't read %s\n(%s)\n", v->blk_device, strerror(errno));
+    if (r != size) LOGE("failed to read \"%s\": %s\n", v->blk_device, strerror(errno));
     mtd_read_close(read);
     if (r != size) return -1;
 
     memcpy(out, &data[write_size * MISC_COMMAND_PAGE], sizeof(*out));
     return 0;
 }
-static int set_bootloader_message_mtd(const struct bootloader_message *in,
+static int set_bootloader_message_mtd(const bootloader_message* in,
                                       const Volume* v) {
     size_t write_size;
     mtd_scan_partitions();
-    const MtdPartition *part = mtd_find_partition_by_name(v->blk_device);
-    if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
-        LOGE("Can't find %s\n", v->blk_device);
+    const MtdPartition* part = mtd_find_partition_by_name(v->blk_device);
+    if (part == nullptr || mtd_partition_info(part, nullptr, nullptr, &write_size)) {
+        LOGE("failed to find \"%s\"\n", v->blk_device);
         return -1;
     }
 
-    MtdReadContext *read = mtd_read_partition(part);
-    if (read == NULL) {
-        LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno));
+    MtdReadContext* read = mtd_read_partition(part);
+    if (read == nullptr) {
+        LOGE("failed to open \"%s\": %s\n", v->blk_device, strerror(errno));
         return -1;
     }
 
     ssize_t size = write_size * MISC_PAGES;
     char data[size];
     ssize_t r = mtd_read_data(read, data, size);
-    if (r != size) LOGE("Can't read %s\n(%s)\n", v->blk_device, strerror(errno));
+    if (r != size) LOGE("failed to read \"%s\": %s\n", v->blk_device, strerror(errno));
     mtd_read_close(read);
     if (r != size) return -1;
 
     memcpy(&data[write_size * MISC_COMMAND_PAGE], in, sizeof(*in));
 
-    MtdWriteContext *write = mtd_write_partition(part);
-    if (write == NULL) {
-        LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno));
+    MtdWriteContext* write = mtd_write_partition(part);
+    if (write == nullptr) {
+        LOGE("failed to open \"%s\": %s\n", v->blk_device, strerror(errno));
         return -1;
     }
     if (mtd_write_data(write, data, size) != size) {
-        LOGE("Can't write %s\n(%s)\n", v->blk_device, strerror(errno));
+        LOGE("failed to write \"%s\": %s\n", v->blk_device, strerror(errno));
         mtd_write_close(write);
         return -1;
     }
     if (mtd_write_close(write)) {
-        LOGE("Can't finish %s\n(%s)\n", v->blk_device, strerror(errno));
+        LOGE("failed to finish \"%s\": %s\n", v->blk_device, strerror(errno));
         return -1;
     }
 
@@ -146,57 +151,67 @@
 static void wait_for_device(const char* fn) {
     int tries = 0;
     int ret;
-    struct stat buf;
     do {
         ++tries;
+        struct stat buf;
         ret = stat(fn, &buf);
-        if (ret) {
-            printf("stat %s try %d: %s\n", fn, tries, strerror(errno));
+        if (ret == -1) {
+            printf("failed to stat \"%s\" try %d: %s\n", fn, tries, strerror(errno));
             sleep(1);
         }
     } while (ret && tries < 10);
+
     if (ret) {
-        printf("failed to stat %s\n", fn);
+        printf("failed to stat \"%s\"\n", fn);
     }
 }
 
-static int get_bootloader_message_block(struct bootloader_message *out,
+static int get_bootloader_message_block(bootloader_message* out,
                                         const Volume* v) {
     wait_for_device(v->blk_device);
     FILE* f = fopen(v->blk_device, "rb");
-    if (f == NULL) {
-        LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno));
+    if (f == nullptr) {
+        LOGE("failed to open \"%s\": %s\n", v->blk_device, strerror(errno));
         return -1;
     }
-    struct bootloader_message temp;
+    bootloader_message temp;
     int count = fread(&temp, sizeof(temp), 1, f);
     if (count != 1) {
-        LOGE("Failed reading %s\n(%s)\n", v->blk_device, strerror(errno));
+        LOGE("failed to read \"%s\": %s\n", v->blk_device, strerror(errno));
         return -1;
     }
     if (fclose(f) != 0) {
-        LOGE("Failed closing %s\n(%s)\n", v->blk_device, strerror(errno));
+        LOGE("failed to close \"%s\": %s\n", v->blk_device, strerror(errno));
         return -1;
     }
     memcpy(out, &temp, sizeof(temp));
     return 0;
 }
 
-static int set_bootloader_message_block(const struct bootloader_message *in,
+static int set_bootloader_message_block(const bootloader_message* in,
                                         const Volume* v) {
     wait_for_device(v->blk_device);
-    FILE* f = fopen(v->blk_device, "wb");
-    if (f == NULL) {
-        LOGE("Can't open %s\n(%s)\n", v->blk_device, strerror(errno));
+    unique_fd fd(open(v->blk_device, O_WRONLY | O_SYNC));
+    if (fd.get() == -1) {
+        LOGE("failed to open \"%s\": %s\n", v->blk_device, strerror(errno));
         return -1;
     }
-    int count = fwrite(in, sizeof(*in), 1, f);
-    if (count != 1) {
-        LOGE("Failed writing %s\n(%s)\n", v->blk_device, strerror(errno));
-        return -1;
+
+    size_t written = 0;
+    const uint8_t* start = reinterpret_cast<const uint8_t*>(in);
+    size_t total = sizeof(*in);
+    while (written < total) {
+        ssize_t wrote = TEMP_FAILURE_RETRY(write(fd.get(), start + written, total - written));
+        if (wrote == -1) {
+            LOGE("failed to write %" PRId64 " bytes: %s\n",
+                 static_cast<off64_t>(written), strerror(errno));
+            return -1;
+        }
+        written += wrote;
     }
-    if (fclose(f) != 0) {
-        LOGE("Failed closing %s\n(%s)\n", v->blk_device, strerror(errno));
+
+    if (fsync(fd.get()) == -1) {
+        LOGE("failed to fsync \"%s\": %s\n", v->blk_device, strerror(errno));
         return -1;
     }
     return 0;
diff --git a/edify/lexer.ll b/edify/lexer.ll
index fb2933b..b764d16 100644
--- a/edify/lexer.ll
+++ b/edify/lexer.ll
@@ -16,6 +16,7 @@
  */
 
 #include <string.h>
+#include <string>
 
 #include "expr.h"
 #include "yydefs.h"
@@ -25,9 +26,7 @@
 int gColumn = 1;
 int gPos = 0;
 
-// TODO: enforce MAX_STRING_LEN during lexing
-char string_buffer[MAX_STRING_LEN];
-char* string_pos;
+std::string string_buffer;
 
 #define ADVANCE do {yylloc.start=gPos; yylloc.end=gPos+yyleng; \
                     gColumn+=yyleng; gPos+=yyleng;} while(0)
@@ -43,7 +42,7 @@
 
 \" {
     BEGIN(STR);
-    string_pos = string_buffer;
+    string_buffer.clear();
     yylloc.start = gPos;
     ++gColumn;
     ++gPos;
@@ -54,36 +53,35 @@
       ++gColumn;
       ++gPos;
       BEGIN(INITIAL);
-      *string_pos = '\0';
-      yylval.str = strdup(string_buffer);
+      yylval.str = strdup(string_buffer.c_str());
       yylloc.end = gPos;
       return STRING;
   }
 
-  \\n   { gColumn += yyleng; gPos += yyleng; *string_pos++ = '\n'; }
-  \\t   { gColumn += yyleng; gPos += yyleng;  *string_pos++ = '\t'; }
-  \\\"  { gColumn += yyleng; gPos += yyleng;  *string_pos++ = '\"'; }
-  \\\\  { gColumn += yyleng; gPos += yyleng;  *string_pos++ = '\\'; }
+  \\n   { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\n'); }
+  \\t   { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\t'); }
+  \\\"  { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\"'); }
+  \\\\  { gColumn += yyleng; gPos += yyleng; string_buffer.push_back('\\'); }
 
   \\x[0-9a-fA-F]{2} {
       gColumn += yyleng;
       gPos += yyleng;
       int val;
       sscanf(yytext+2, "%x", &val);
-      *string_pos++ = val;
+      string_buffer.push_back(static_cast<char>(val));
   }
 
   \n {
       ++gLine;
       ++gPos;
       gColumn = 1;
-      *string_pos++ = yytext[0];
+      string_buffer.push_back(yytext[0]);
   }
 
   . {
       ++gColumn;
       ++gPos;
-      *string_pos++ = yytext[0];
+      string_buffer.push_back(yytext[0]);
   }
 }
 
diff --git a/edify/main.cpp b/edify/main.cpp
index b1baa0b..6007a3d 100644
--- a/edify/main.cpp
+++ b/edify/main.cpp
@@ -18,6 +18,8 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <string>
+
 #include "expr.h"
 #include "parser.h"
 
@@ -151,6 +153,9 @@
     expect("greater_than_int(x, 3)", "", &errors);
     expect("greater_than_int(3, x)", "", &errors);
 
+    // big string
+    expect(std::string(8192, 's').c_str(), std::string(8192, 's').c_str(), &errors);
+
     printf("\n");
 
     return errors;
diff --git a/install.cpp b/install.cpp
index 7d88ed7..33c1f54 100644
--- a/install.cpp
+++ b/install.cpp
@@ -23,6 +23,8 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <vector>
+
 #include "common.h"
 #include "install.h"
 #include "mincrypt/rsa.h"
@@ -122,20 +124,20 @@
     //   - the name of the package zip file.
     //
 
-    const char** args = (const char**)malloc(sizeof(char*) * 5);
+    const char* args[5];
     args[0] = binary;
     args[1] = EXPAND(RECOVERY_API_VERSION);   // defined in Android.mk
-    char* temp = (char*)malloc(10);
-    sprintf(temp, "%d", pipefd[1]);
+    char temp[16];
+    snprintf(temp, sizeof(temp), "%d", pipefd[1]);
     args[2] = temp;
-    args[3] = (char*)path;
+    args[3] = path;
     args[4] = NULL;
 
     pid_t pid = fork();
     if (pid == 0) {
         umask(022);
         close(pipefd[0]);
-        execv(binary, (char* const*)args);
+        execv(binary, const_cast<char**>(args));
         fprintf(stdout, "E:Can't run %s (%s)\n", binary, strerror(errno));
         _exit(-1);
     }
@@ -221,19 +223,16 @@
         return INSTALL_CORRUPT;
     }
 
-    int numKeys;
-    Certificate* loadedKeys = load_keys(PUBLIC_KEYS_FILE, &numKeys);
-    if (loadedKeys == NULL) {
+    std::vector<Certificate> loadedKeys;
+    if (!load_keys(PUBLIC_KEYS_FILE, loadedKeys)) {
         LOGE("Failed to load keys\n");
         return INSTALL_CORRUPT;
     }
-    LOGI("%d key(s) loaded from %s\n", numKeys, PUBLIC_KEYS_FILE);
+    LOGI("%zu key(s) loaded from %s\n", loadedKeys.size(), PUBLIC_KEYS_FILE);
 
     ui->Print("Verifying update package...\n");
 
-    int err;
-    err = verify_file(map.addr, map.length, loadedKeys, numKeys);
-    free(loadedKeys);
+    int err = verify_file(map.addr, map.length, loadedKeys);
     LOGI("verify_file returned %d\n", err);
     if (err != VERIFY_SUCCESS) {
         LOGE("signature verification failed\n");
diff --git a/minadbd/services.cpp b/minadbd/services.cpp
index d25648f..658a43f 100644
--- a/minadbd/services.cpp
+++ b/minadbd/services.cpp
@@ -35,11 +35,10 @@
     void *cookie;
 };
 
-void* service_bootstrap_func(void* x) {
+void service_bootstrap_func(void* x) {
     stinfo* sti = reinterpret_cast<stinfo*>(x);
     sti->func(sti->fd, sti->cookie);
     free(sti);
-    return 0;
 }
 
 static void sideload_host_service(int sfd, void* data) {
diff --git a/minui/resources.cpp b/minui/resources.cpp
index 63a0dff..fdbe554 100644
--- a/minui/resources.cpp
+++ b/minui/resources.cpp
@@ -28,6 +28,7 @@
 #include <linux/fb.h>
 #include <linux/kd.h>
 
+#include <vector>
 #include <png.h>
 
 #include "minui.h"
@@ -398,18 +399,13 @@
     png_infop info_ptr = NULL;
     png_uint_32 width, height;
     png_byte channels;
-    unsigned char* row;
     png_uint_32 y;
+    std::vector<unsigned char> row;
 
     *pSurface = NULL;
 
     if (locale == NULL) {
-        surface = malloc_surface(0);
-        surface->width = 0;
-        surface->height = 0;
-        surface->row_bytes = 0;
-        surface->pixel_bytes = 1;
-        goto exit;
+        return result;
     }
 
     result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
@@ -420,13 +416,13 @@
         goto exit;
     }
 
-    row = reinterpret_cast<unsigned char*>(malloc(width));
+    row.resize(width);
     for (y = 0; y < height; ++y) {
-        png_read_row(png_ptr, row, NULL);
+        png_read_row(png_ptr, row.data(), NULL);
         int w = (row[1] << 8) | row[0];
         int h = (row[3] << 8) | row[2];
         int len = row[4];
-        char* loc = (char*)row+5;
+        char* loc = reinterpret_cast<char*>(&row[5]);
 
         if (y+1+h >= height || matches_locale(loc, locale)) {
             printf("  %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
@@ -443,8 +439,8 @@
 
             int i;
             for (i = 0; i < h; ++i, ++y) {
-                png_read_row(png_ptr, row, NULL);
-                memcpy(surface->data + i*w, row, w);
+                png_read_row(png_ptr, row.data(), NULL);
+                memcpy(surface->data + i*w, row.data(), w);
             }
 
             *pSurface = reinterpret_cast<GRSurface*>(surface);
@@ -452,7 +448,7 @@
         } else {
             int i;
             for (i = 0; i < h; ++i, ++y) {
-                png_read_row(png_ptr, row, NULL);
+                png_read_row(png_ptr, row.data(), NULL);
             }
         }
     }
diff --git a/minzip/Android.mk b/minzip/Android.mk
index 22eabfb..3d36fd6 100644
--- a/minzip/Android.mk
+++ b/minzip/Android.mk
@@ -4,7 +4,7 @@
 LOCAL_SRC_FILES := \
 	Hash.c \
 	SysUtil.c \
-	DirUtil.c \
+	DirUtil.cpp \
 	Inlines.c \
 	Zip.c
 
diff --git a/minzip/DirUtil.c b/minzip/DirUtil.cpp
similarity index 78%
rename from minzip/DirUtil.c
rename to minzip/DirUtil.cpp
index 97cb2e0..823b6ed 100644
--- a/minzip/DirUtil.c
+++ b/minzip/DirUtil.cpp
@@ -24,6 +24,8 @@
 #include <dirent.h>
 #include <limits.h>
 
+#include <string>
+
 #include "DirUtil.h"
 
 typedef enum { DMISSING, DDIR, DILLEGAL } DirStatus;
@@ -66,43 +68,25 @@
         errno = ENOENT;
         return -1;
     }
-
-    /* Allocate a path that we can modify; stick a slash on
-     * the end to make things easier.
-     */
-    size_t pathLen = strlen(path);
-    char *cpath = (char *)malloc(pathLen + 2);
-    if (cpath == NULL) {
-        errno = ENOMEM;
-        return -1;
-    }
-    memcpy(cpath, path, pathLen);
+    // Allocate a path that we can modify; stick a slash on
+    // the end to make things easier.
+    std::string cpath = path;
     if (stripFileName) {
-        /* Strip everything after the last slash.
-         */
-        char *c = cpath + pathLen - 1;
-        while (c != cpath && *c != '/') {
-            c--;
-        }
-        if (c == cpath) {
-            //xxx test this path
-            /* No directory component.  Act like the path was empty.
-             */
+        // Strip everything after the last slash.
+        size_t pos = cpath.rfind('/');
+        if (pos == std::string::npos) {
             errno = ENOENT;
-            free(cpath);
             return -1;
         }
-        c[1] = '\0';    // Terminate after the slash we found.
+        cpath.resize(pos + 1);
     } else {
-        /* Make sure that the path ends in a slash.
-         */
-        cpath[pathLen] = '/';
-        cpath[pathLen + 1] = '\0';
+        // Make sure that the path ends in a slash.
+        cpath.push_back('/');
     }
 
     /* See if it already exists.
      */
-    ds = getPathDirStatus(cpath);
+    ds = getPathDirStatus(cpath.c_str());
     if (ds == DDIR) {
         return 0;
     } else if (ds == DILLEGAL) {
@@ -112,7 +96,8 @@
     /* Walk up the path from the root and make each level.
      * If a directory already exists, no big deal.
      */
-    char *p = cpath;
+    const char *path_start = &cpath[0];
+    char *p = &cpath[0];
     while (*p != '\0') {
         /* Skip any slashes, watching out for the end of the string.
          */
@@ -135,12 +120,11 @@
         /* Check this part of the path and make a new directory
          * if necessary.
          */
-        ds = getPathDirStatus(cpath);
+        ds = getPathDirStatus(path_start);
         if (ds == DILLEGAL) {
             /* Could happen if some other process/thread is
              * messing with the filesystem.
              */
-            free(cpath);
             return -1;
         } else if (ds == DMISSING) {
             int err;
@@ -148,11 +132,11 @@
             char *secontext = NULL;
 
             if (sehnd) {
-                selabel_lookup(sehnd, &secontext, cpath, mode);
+                selabel_lookup(sehnd, &secontext, path_start, mode);
                 setfscreatecon(secontext);
             }
 
-            err = mkdir(cpath, mode);
+            err = mkdir(path_start, mode);
 
             if (secontext) {
                 freecon(secontext);
@@ -160,22 +144,17 @@
             }
 
             if (err != 0) {
-                free(cpath);
                 return -1;
             }
-            if (timestamp != NULL && utime(cpath, timestamp)) {
-                free(cpath);
+            if (timestamp != NULL && utime(path_start, timestamp)) {
                 return -1;
             }
         }
         // else, this directory already exists.
-        
-        /* Repair the path and continue.
-         */
+
+        // Repair the path and continue.
         *p = '/';
     }
-    free(cpath);
-
     return 0;
 }
 
diff --git a/otafault/Android.mk b/otafault/Android.mk
new file mode 100644
index 0000000..75617a1
--- /dev/null
+++ b/otafault/Android.mk
@@ -0,0 +1,58 @@
+# Copyright 2015 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 languae governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+empty :=
+space := $(empty) $(empty)
+comma := ,
+
+ifneq ($(TARGET_INJECT_FAULTS),)
+TARGET_INJECT_FAULTS := $(subst $(comma),$(space),$(strip $(TARGET_INJECT_FAULTS)))
+endif
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := ota_io.cpp
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULE := libotafault
+LOCAL_CLANG := true
+
+ifneq ($(TARGET_INJECT_FAULTS),)
+$(foreach ft,$(TARGET_INJECT_FAULTS),\
+	$(eval LOCAL_CFLAGS += -DTARGET_$(ft)_FAULT=$(TARGET_$(ft)_FAULT_FILE)))
+LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_CFLAGS += -DTARGET_INJECT_FAULTS
+endif
+
+LOCAL_STATIC_LIBRARIES := libc
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := ota_io.cpp test.cpp
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE := otafault_test
+LOCAL_STATIC_LIBRARIES := libc
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_CFLAGS += -Wno-unused-parameter -Wno-writable-strings
+
+ifneq ($(TARGET_INJECT_FAULTS),)
+$(foreach ft,$(TARGET_INJECT_FAULTS),\
+	$(eval LOCAL_CFLAGS += -DTARGET_$(ft)_FAULT=$(TARGET_$(ft)_FAULT_FILE)))
+LOCAL_CFLAGS += -DTARGET_INJECT_FAULTS
+endif
+
+include $(BUILD_EXECUTABLE)
diff --git a/otafault/ota_io.cpp b/otafault/ota_io.cpp
new file mode 100644
index 0000000..02e80f9
--- /dev/null
+++ b/otafault/ota_io.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#if defined (TARGET_INJECT_FAULTS)
+#include <map>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "ota_io.h"
+
+#if defined (TARGET_INJECT_FAULTS)
+static std::map<int, const char*> FilenameCache;
+static std::string FaultFileName =
+#if defined (TARGET_READ_FAULT)
+        TARGET_READ_FAULT;
+#elif defined (TARGET_WRITE_FAULT)
+        TARGET_WRITE_FAULT;
+#elif defined (TARGET_FSYNC_FAULT)
+        TARGET_FSYNC_FAULT;
+#endif // defined (TARGET_READ_FAULT)
+#endif // defined (TARGET_INJECT_FAULTS)
+
+int ota_open(const char* path, int oflags) {
+#if defined (TARGET_INJECT_FAULTS)
+    // Let the caller handle errors; we do not care if open succeeds or fails
+    int fd = open(path, oflags);
+    FilenameCache[fd] = path;
+    return fd;
+#else
+    return open(path, oflags);
+#endif
+}
+
+int ota_open(const char* path, int oflags, mode_t mode) {
+#if defined (TARGET_INJECT_FAULTS)
+    int fd = open(path, oflags, mode);
+    FilenameCache[fd] = path;
+    return fd;
+#else
+    return open(path, oflags, mode);
+#endif
+}
+
+FILE* ota_fopen(const char* path, const char* mode) {
+#if defined (TARGET_INJECT_FAULTS)
+    FILE* fh = fopen(path, mode);
+    FilenameCache[(intptr_t)fh] = path;
+    return fh;
+#else
+    return fopen(path, mode);
+#endif
+}
+
+int ota_close(int fd) {
+#if defined (TARGET_INJECT_FAULTS)
+    // descriptors can be reused, so make sure not to leave them in the cahce
+    FilenameCache.erase(fd);
+#endif
+    return close(fd);
+}
+
+int ota_fclose(FILE* fh) {
+#if defined (TARGET_INJECT_FAULTS)
+    FilenameCache.erase((intptr_t)fh);
+#endif
+    return fclose(fh);
+}
+
+size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream) {
+#if defined (TARGET_READ_FAULT)
+    if (FilenameCache.find((intptr_t)stream) != FilenameCache.end()
+            && FilenameCache[(intptr_t)stream] == FaultFileName) {
+        FaultFileName = "";
+        errno = EIO;
+        return 0;
+    } else {
+        return fread(ptr, size, nitems, stream);
+    }
+#else
+    return fread(ptr, size, nitems, stream);
+#endif
+}
+
+ssize_t ota_read(int fd, void* buf, size_t nbyte) {
+#if defined (TARGET_READ_FAULT)
+    if (FilenameCache.find(fd) != FilenameCache.end()
+            && FilenameCache[fd] == FaultFileName) {
+        FaultFileName = "";
+        errno = EIO;
+        return -1;
+    } else {
+        return read(fd, buf, nbyte);
+    }
+#else
+    return read(fd, buf, nbyte);
+#endif
+}
+
+size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream) {
+#if defined (TARGET_WRITE_FAULT)
+    if (FilenameCache.find((intptr_t)stream) != FilenameCache.end()
+            && FilenameCache[(intptr_t)stream] == FaultFileName) {
+        FaultFileName = "";
+        errno = EIO;
+        return 0;
+    } else {
+        return fwrite(ptr, size, count, stream);
+    }
+#else
+    return fwrite(ptr, size, count, stream);
+#endif
+}
+
+ssize_t ota_write(int fd, const void* buf, size_t nbyte) {
+#if defined (TARGET_WRITE_FAULT)
+    if (FilenameCache.find(fd) != FilenameCache.end()
+            && FilenameCache[fd] == FaultFileName) {
+        FaultFileName = "";
+        errno = EIO;
+        return -1;
+    } else {
+        return write(fd, buf, nbyte);
+    }
+#else
+    return write(fd, buf, nbyte);
+#endif
+}
+
+int ota_fsync(int fd) {
+#if defined (TARGET_FSYNC_FAULT)
+    if (FilenameCache.find(fd) != FilenameCache.end()
+            && FilenameCache[fd] == FaultFileName) {
+        FaultFileName = "";
+        errno = EIO;
+        return -1;
+    } else {
+        return fsync(fd);
+    }
+#else
+    return fsync(fd);
+#endif
+}
diff --git a/otafault/ota_io.h b/otafault/ota_io.h
new file mode 100644
index 0000000..641a5ae
--- /dev/null
+++ b/otafault/ota_io.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+/*
+ * Provide a series of proxy functions for basic file accessors.
+ * The behavior of these functions can be changed to return different
+ * errors under a variety of conditions.
+ */
+
+#ifndef _UPDATER_OTA_IO_H_
+#define _UPDATER_OTA_IO_H_
+
+#include <stdio.h>
+#include <sys/stat.h>
+
+int ota_open(const char* path, int oflags);
+
+int ota_open(const char* path, int oflags, mode_t mode);
+
+FILE* ota_fopen(const char* filename, const char* mode);
+
+int ota_close(int fd);
+
+int ota_fclose(FILE* fh);
+
+size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream);
+
+ssize_t ota_read(int fd, void* buf, size_t nbyte);
+
+size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream);
+
+ssize_t ota_write(int fd, const void* buf, size_t nbyte);
+
+int ota_fsync(int fd);
+
+#endif
diff --git a/otafault/test.cpp b/otafault/test.cpp
new file mode 100644
index 0000000..a0f7315
--- /dev/null
+++ b/otafault/test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include "ota_io.h"
+
+int main(int argc, char **argv) {
+    int fd = open("testdata/test.file", O_RDWR);
+    char buf[8];
+    char *out = "321";
+    int readv = ota_read(fd, buf, 4);
+    printf("Read returned %d\n", readv);
+    int writev = ota_write(fd, out, 4);
+    printf("Write returned %d\n", writev);
+    return 0;
+}
diff --git a/print_sha1.h b/print_sha1.h
index 9e37c5f..fa3d7e0 100644
--- a/print_sha1.h
+++ b/print_sha1.h
@@ -20,9 +20,9 @@
 #include <stdint.h>
 #include <string>
 
-#include "mincrypt/sha.h"
+#include "openssl/sha.h"
 
-static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_SIZE], size_t len) {
+static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH], size_t len) {
     const char* hex = "0123456789abcdef";
     std::string result = "";
     for (size_t i = 0; i < len; ++i) {
@@ -32,11 +32,11 @@
     return result;
 }
 
-static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_SIZE]) {
-    return print_sha1(sha1, SHA_DIGEST_SIZE);
+static std::string print_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) {
+    return print_sha1(sha1, SHA_DIGEST_LENGTH);
 }
 
-static std::string short_sha1(const uint8_t sha1[SHA_DIGEST_SIZE]) {
+static std::string short_sha1(const uint8_t sha1[SHA_DIGEST_LENGTH]) {
     return print_sha1(sha1, 4);
 }
 
diff --git a/tests/Android.mk b/tests/Android.mk
index 4ce00b4..262fb8b 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -16,11 +16,40 @@
 
 LOCAL_PATH := $(call my-dir)
 
+# Unit tests
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_MODULE := recovery_unit_test
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_STATIC_LIBRARIES := libverifier
+LOCAL_SRC_FILES := unit/asn1_decoder_test.cpp
+LOCAL_C_INCLUDES := bootable/recovery
+include $(BUILD_NATIVE_TEST)
+
+# Component tests
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_STATIC_LIBRARIES := libverifier
-LOCAL_SRC_FILES := asn1_decoder_test.cpp
-LOCAL_MODULE := asn1_decoder_test
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/..
+LOCAL_MODULE := recovery_component_test
+LOCAL_C_INCLUDES := bootable/recovery
+LOCAL_SRC_FILES := component/verifier_test.cpp
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_STATIC_LIBRARIES := \
+    libbase \
+    libverifier \
+    libmincrypt \
+    libminui \
+    libminzip \
+    libcutils \
+    libc
+
+testdata_out_path := $(TARGET_OUT_DATA_NATIVE_TESTS)/recovery
+testdata_files := $(call find-subdir-files, testdata/*)
+
+GEN := $(addprefix $(testdata_out_path)/, $(testdata_files))
+$(GEN): PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@
+$(GEN): $(testdata_out_path)/% : $(LOCAL_PATH)/%
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(GEN)
 include $(BUILD_NATIVE_TEST)
diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp
new file mode 100644
index 0000000..d6f1e0b
--- /dev/null
+++ b/tests/component/verifier_test.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2009 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 agree to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+
+#include "common.h"
+#include "mincrypt/sha.h"
+#include "mincrypt/sha256.h"
+#include "minzip/SysUtil.h"
+#include "ui.h"
+#include "verifier.h"
+
+#if defined(__LP64__)
+#define NATIVE_TEST_PATH "/nativetest64"
+#else
+#define NATIVE_TEST_PATH "/nativetest"
+#endif
+
+static const char* DATA_PATH = getenv("ANDROID_DATA");
+static const char* TESTDATA_PATH = "/recovery/testdata/";
+
+// This is build/target/product/security/testkey.x509.pem after being
+// dumped out by dumpkey.jar.
+RSAPublicKey test_key =
+    { 64, 0xc926ad21,
+      { 0x6afee91fu, 0x7fa31d5bu, 0x38a0b217u, 0x99df9baeu,
+        0xfe72991du, 0x727d3c04u, 0x20943f99u, 0xd08e7826u,
+        0x69e7c8a2u, 0xdeeccc8eu, 0x6b9af76fu, 0x553311c4u,
+        0x07b9e247u, 0x54c8bbcau, 0x6a540d81u, 0x48dbf567u,
+        0x98c92877u, 0x134fbfdeu, 0x01b32564u, 0x24581948u,
+        0x6cddc3b8u, 0x0cd444dau, 0xfe0381ccu, 0xf15818dfu,
+        0xc06e6d42u, 0x2e2f6412u, 0x093a6737u, 0x94d83b31u,
+        0xa466c87au, 0xb3f284a0u, 0xa694ec2cu, 0x053359e6u,
+        0x9717ee6au, 0x0732e080u, 0x220d5008u, 0xdc4af350u,
+        0x93d0a7c3u, 0xe330c9eau, 0xcac3da1eu, 0x8ebecf8fu,
+        0xc2be387fu, 0x38a14e89u, 0x211586f0u, 0x18b846f5u,
+        0x43be4c72u, 0xb578c204u, 0x1bbfb230u, 0xf1e267a8u,
+        0xa2d3e656u, 0x64b8e4feu, 0xe7e83d4bu, 0x3e77a943u,
+        0x3559ffd9u, 0x0ebb0f99u, 0x0aa76ce6u, 0xd3786ea7u,
+        0xbca8cd6bu, 0x068ca8e8u, 0xeb1de2ffu, 0x3e3ecd6cu,
+        0xe0d9d825u, 0xb1edc762u, 0xdec60b24u, 0xd6931904u},
+      { 0xccdcb989u, 0xe19281f9u, 0xa6e80accu, 0xb7f40560u,
+        0x0efb0bccu, 0x7f12b0bbu, 0x1e90531au, 0x136d95d0u,
+        0x9e660665u, 0x7d54918fu, 0xe3b93ea2u, 0x2f415d10u,
+        0x3d2df6e6u, 0x7a627ecfu, 0xa6f22d70u, 0xb995907au,
+        0x09de16b2u, 0xfeb8bd61u, 0xf24ec294u, 0x716a427fu,
+        0x2e12046fu, 0xeaf3d56au, 0xd9b873adu, 0x0ced340bu,
+        0xbc9cec09u, 0x73c65903u, 0xee39ce9bu, 0x3eede25au,
+        0x397633b7u, 0x2583c165u, 0x8514f97du, 0xe9166510u,
+        0x0b6fae99u, 0xa47139fdu, 0xdb8352f0u, 0xb2ad7f2cu,
+        0xa11552e2u, 0xd4d490a7u, 0xe11e8568u, 0xe9e484dau,
+        0xd3ef8449u, 0xa47055dau, 0x4edd9557u, 0x03a78ba1u,
+        0x770e130du, 0x16762facu, 0x0cbdfcc4u, 0xf3070540u,
+        0x008b6515u, 0x60e7e1b7u, 0xa72cf7f9u, 0xaff86e39u,
+        0x4296faadu, 0xfc90430eu, 0x6cc8f377u, 0xb398fd43u,
+        0x423c5997u, 0x991d59c4u, 0x6464bf73u, 0x96431575u,
+        0x15e3d207u, 0x30532a7au, 0x8c4be618u, 0x460a4d76u },
+      3
+    };
+
+RSAPublicKey test_f4_key =
+    { 64, 0xc9bd1f21,
+      { 0x1178db1fu, 0xbf5d0e55u, 0x3393a165u, 0x0ef4c287u,
+        0xbc472a4au, 0x383fc5a1u, 0x4a13b7d2u, 0xb1ff2ac3u,
+        0xaf66b4d9u, 0x9280acefu, 0xa2165bdbu, 0x6a4d6e5cu,
+        0x08ea676bu, 0xb7ac70c7u, 0xcd158139u, 0xa635ccfeu,
+        0xa46ab8a8u, 0x445a3e8bu, 0xdc81d9bbu, 0x91ce1a20u,
+        0x68021cdeu, 0x4516eda9u, 0x8d43c30cu, 0xed1eff14u,
+        0xca387e4cu, 0x58adc233u, 0x4657ab27u, 0xa95b521eu,
+        0xdfc0e30cu, 0x394d64a1u, 0xc6b321a1u, 0x2ca22cb8u,
+        0xb1892d5cu, 0x5d605f3eu, 0x6025483cu, 0x9afd5181u,
+        0x6e1a7105u, 0x03010593u, 0x70acd304u, 0xab957cbfu,
+        0x8844abbbu, 0x53846837u, 0x24e98a43u, 0x2ba060c1u,
+        0x8b88b88eu, 0x44eea405u, 0xb259fc41u, 0x0907ad9cu,
+        0x13003adau, 0xcf79634eu, 0x7d314ec9u, 0xfbbe4c2bu,
+        0xd84d0823u, 0xfd30fd88u, 0x68d8a909u, 0xfb4572d9u,
+        0xa21301c2u, 0xd00a4785u, 0x6862b50cu, 0xcfe49796u,
+        0xdaacbd83u, 0xfb620906u, 0xdf71e0ccu, 0xbbc5b030u },
+      { 0x69a82189u, 0x1a8b22f4u, 0xcf49207bu, 0x68cc056au,
+        0xb206b7d2u, 0x1d449bbdu, 0xe9d342f2u, 0x29daea58u,
+        0xb19d011au, 0xc62f15e4u, 0x9452697au, 0xb62bb87eu,
+        0x60f95cc2u, 0x279ebb2du, 0x17c1efd8u, 0xec47558bu,
+        0xc81334d1u, 0x88fe7601u, 0x79992eb1u, 0xb4555615u,
+        0x2022ac8cu, 0xc79a4b8cu, 0xb288b034u, 0xd6b942f0u,
+        0x0caa32fbu, 0xa065ba51u, 0x4de9f154u, 0x29f64f6cu,
+        0x7910af5eu, 0x3ed4636au, 0xe4c81911u, 0x9183f37du,
+        0x5811e1c4u, 0x29c7a58cu, 0x9715d4d3u, 0xc7e2dce3u,
+        0x140972ebu, 0xf4c8a69eu, 0xa104d424u, 0x5dabbdfbu,
+        0x41cb4c6bu, 0xd7f44717u, 0x61785ff7u, 0x5e0bc273u,
+        0x36426c70u, 0x2aa6f08eu, 0x083badbfu, 0x3cab941bu,
+        0x8871da23u, 0x1ab3dbaeu, 0x7115a21du, 0xf5aa0965u,
+        0xf766f562u, 0x7f110225u, 0x86d96a04u, 0xc50a120eu,
+        0x3a751ca3u, 0xc21aa186u, 0xba7359d0u, 0x3ff2b257u,
+        0xd116e8bbu, 0xfc1318c0u, 0x070e5b1du, 0x83b759a6u },
+      65537
+    };
+
+ECPublicKey test_ec_key =
+    {
+       {
+         {0xd656fa24u, 0x931416cau, 0x1c0278c6u, 0x174ebe4cu,
+          0x6018236au, 0x45ba1656u, 0xe8c05d84u, 0x670ed500u}
+      },
+      {
+        {0x0d179adeu, 0x4c16827du, 0x9f8cb992u, 0x8f69ff8au,
+         0x481b1020u, 0x798d91afu, 0x184db8e9u, 0xb5848dd9u}
+      }
+    };
+
+RecoveryUI* ui = NULL;
+
+class MockUI : public RecoveryUI {
+    void Init() { }
+    void SetStage(int, int) { }
+    void SetLocale(const char*) { }
+    void SetBackground(Icon /*icon*/) { }
+
+    void SetProgressType(ProgressType /*determinate*/) { }
+    void ShowProgress(float /*portion*/, float /*seconds*/) { }
+    void SetProgress(float /*fraction*/) { }
+
+    void ShowText(bool /*visible*/) { }
+    bool IsTextVisible() { return false; }
+    bool WasTextEverVisible() { return false; }
+    void Print(const char* fmt, ...) {
+        va_list ap;
+        va_start(ap, fmt);
+        vfprintf(stderr, fmt, ap);
+        va_end(ap);
+    }
+    void PrintOnScreenOnly(const char* fmt, ...) {
+        va_list ap;
+        va_start(ap, fmt);
+        vfprintf(stderr, fmt, ap);
+        va_end(ap);
+    }
+    void ShowFile(const char*) { }
+
+    void StartMenu(const char* const* /*headers*/,
+                   const char* const* /*items*/,
+                   int /*initial_selection*/) { }
+    int SelectMenu(int /*sel*/) { return 0; }
+    void EndMenu() { }
+};
+
+void
+ui_print(const char* format, ...) {
+    va_list ap;
+    va_start(ap, format);
+    vfprintf(stdout, format, ap);
+    va_end(ap);
+}
+
+class VerifierTest : public testing::TestWithParam<std::vector<std::string>> {
+  public:
+    MemMapping memmap;
+    std::vector<Certificate> certs;
+
+    virtual void SetUp() {
+        std::vector<std::string> args = GetParam();
+        std::string package = android::base::StringPrintf("%s%s%s%s", DATA_PATH, NATIVE_TEST_PATH,
+                TESTDATA_PATH, args[0].c_str());
+        for (auto it = ++(args.cbegin()); it != args.cend(); ++it) {
+            if (it->substr(it->length() - 3, it->length()) == "256") {
+                if (certs.empty()) {
+                    FAIL() << "May only specify -sha256 after key type\n";
+                }
+                certs.back().hash_len = SHA256_DIGEST_SIZE;
+            } else if (*it == "ec") {
+                certs.emplace_back(SHA_DIGEST_SIZE, Certificate::EC,
+                        nullptr, std::unique_ptr<ECPublicKey>(new ECPublicKey(test_ec_key)));
+            } else if (*it == "e3") {
+                certs.emplace_back(SHA_DIGEST_SIZE, Certificate::RSA,
+                        std::unique_ptr<RSAPublicKey>(new RSAPublicKey(test_key)), nullptr);
+            } else if (*it == "f4") {
+                certs.emplace_back(SHA_DIGEST_SIZE, Certificate::RSA,
+                        std::unique_ptr<RSAPublicKey>(new RSAPublicKey(test_f4_key)), nullptr);
+            }
+        }
+        if (certs.empty()) {
+            certs.emplace_back(SHA_DIGEST_SIZE, Certificate::RSA,
+                    std::unique_ptr<RSAPublicKey>(new RSAPublicKey(test_key)), nullptr);
+        }
+        if (sysMapFile(package.c_str(), &memmap) != 0) {
+            FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n";
+        }
+    }
+
+    static void SetUpTestCase() {
+        ui = new MockUI();
+    }
+};
+
+class VerifierSuccessTest : public VerifierTest {
+};
+
+class VerifierFailureTest : public VerifierTest {
+};
+
+TEST_P(VerifierSuccessTest, VerifySucceed) {
+    ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs), VERIFY_SUCCESS);
+}
+
+TEST_P(VerifierFailureTest, VerifyFailure) {
+    ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs), VERIFY_FAILURE);
+}
+
+INSTANTIATE_TEST_CASE_P(SingleKeySuccess, VerifierSuccessTest,
+        ::testing::Values(
+            std::vector<std::string>({"otasigned.zip", "e3"}),
+            std::vector<std::string>({"otasigned_f4.zip", "f4"}),
+            std::vector<std::string>({"otasigned_sha256.zip", "e3", "sha256"}),
+            std::vector<std::string>({"otasigned_f4_sha256.zip", "f4", "sha256"}),
+            std::vector<std::string>({"otasigned_ecdsa_sha256.zip", "ec", "sha256"})));
+
+INSTANTIATE_TEST_CASE_P(MultiKeySuccess, VerifierSuccessTest,
+        ::testing::Values(
+            std::vector<std::string>({"otasigned.zip", "f4", "e3"}),
+            std::vector<std::string>({"otasigned_f4.zip", "ec", "f4"}),
+            std::vector<std::string>({"otasigned_sha256.zip", "ec", "e3", "e3", "sha256"}),
+            std::vector<std::string>({"otasigned_f4_sha256.zip", "ec", "sha256", "e3", "f4", "sha256"}),
+            std::vector<std::string>({"otasigned_ecdsa_sha256.zip", "f4", "sha256", "e3", "ec", "sha256"})));
+
+INSTANTIATE_TEST_CASE_P(WrongKey, VerifierFailureTest,
+        ::testing::Values(
+            std::vector<std::string>({"otasigned.zip", "f4"}),
+            std::vector<std::string>({"otasigned_f4.zip", "e3"}),
+            std::vector<std::string>({"otasigned_ecdsa_sha256.zip", "e3", "sha256"})));
+
+INSTANTIATE_TEST_CASE_P(WrongHash, VerifierFailureTest,
+        ::testing::Values(
+            std::vector<std::string>({"otasigned.zip", "e3", "sha256"}),
+            std::vector<std::string>({"otasigned_f4.zip", "f4", "sha256"}),
+            std::vector<std::string>({"otasigned_sha256.zip"}),
+            std::vector<std::string>({"otasigned_f4_sha256.zip", "f4"}),
+            std::vector<std::string>({"otasigned_ecdsa_sha256.zip"})));
+
+INSTANTIATE_TEST_CASE_P(BadPackage, VerifierFailureTest,
+        ::testing::Values(
+            std::vector<std::string>({"random.zip"}),
+            std::vector<std::string>({"fake-eocd.zip"}),
+            std::vector<std::string>({"alter-metadata.zip"}),
+            std::vector<std::string>({"alter-footer.zip"})));
diff --git a/testdata/alter-footer.zip b/tests/testdata/alter-footer.zip
similarity index 100%
rename from testdata/alter-footer.zip
rename to tests/testdata/alter-footer.zip
Binary files differ
diff --git a/testdata/alter-metadata.zip b/tests/testdata/alter-metadata.zip
similarity index 100%
rename from testdata/alter-metadata.zip
rename to tests/testdata/alter-metadata.zip
Binary files differ
diff --git a/testdata/fake-eocd.zip b/tests/testdata/fake-eocd.zip
similarity index 100%
rename from testdata/fake-eocd.zip
rename to tests/testdata/fake-eocd.zip
Binary files differ
diff --git a/testdata/jarsigned.zip b/tests/testdata/jarsigned.zip
similarity index 100%
rename from testdata/jarsigned.zip
rename to tests/testdata/jarsigned.zip
Binary files differ
diff --git a/testdata/otasigned.zip b/tests/testdata/otasigned.zip
similarity index 100%
rename from testdata/otasigned.zip
rename to tests/testdata/otasigned.zip
Binary files differ
diff --git a/testdata/otasigned_ecdsa_sha256.zip b/tests/testdata/otasigned_ecdsa_sha256.zip
similarity index 100%
rename from testdata/otasigned_ecdsa_sha256.zip
rename to tests/testdata/otasigned_ecdsa_sha256.zip
Binary files differ
diff --git a/testdata/otasigned_f4.zip b/tests/testdata/otasigned_f4.zip
similarity index 100%
rename from testdata/otasigned_f4.zip
rename to tests/testdata/otasigned_f4.zip
Binary files differ
diff --git a/testdata/otasigned_f4_sha256.zip b/tests/testdata/otasigned_f4_sha256.zip
similarity index 100%
rename from testdata/otasigned_f4_sha256.zip
rename to tests/testdata/otasigned_f4_sha256.zip
Binary files differ
diff --git a/testdata/otasigned_sha256.zip b/tests/testdata/otasigned_sha256.zip
similarity index 100%
rename from testdata/otasigned_sha256.zip
rename to tests/testdata/otasigned_sha256.zip
Binary files differ
diff --git a/testdata/random.zip b/tests/testdata/random.zip
similarity index 100%
rename from testdata/random.zip
rename to tests/testdata/random.zip
Binary files differ
diff --git a/testdata/test_f4.pk8 b/tests/testdata/test_f4.pk8
similarity index 100%
rename from testdata/test_f4.pk8
rename to tests/testdata/test_f4.pk8
Binary files differ
diff --git a/testdata/test_f4.x509.pem b/tests/testdata/test_f4.x509.pem
similarity index 100%
rename from testdata/test_f4.x509.pem
rename to tests/testdata/test_f4.x509.pem
diff --git a/testdata/test_f4_sha256.x509.pem b/tests/testdata/test_f4_sha256.x509.pem
similarity index 100%
rename from testdata/test_f4_sha256.x509.pem
rename to tests/testdata/test_f4_sha256.x509.pem
diff --git a/testdata/testkey.pk8 b/tests/testdata/testkey.pk8
similarity index 100%
rename from testdata/testkey.pk8
rename to tests/testdata/testkey.pk8
Binary files differ
diff --git a/testdata/testkey.x509.pem b/tests/testdata/testkey.x509.pem
similarity index 100%
rename from testdata/testkey.x509.pem
rename to tests/testdata/testkey.x509.pem
diff --git a/testdata/testkey_ecdsa.pk8 b/tests/testdata/testkey_ecdsa.pk8
similarity index 100%
rename from testdata/testkey_ecdsa.pk8
rename to tests/testdata/testkey_ecdsa.pk8
Binary files differ
diff --git a/testdata/testkey_ecdsa.x509.pem b/tests/testdata/testkey_ecdsa.x509.pem
similarity index 100%
rename from testdata/testkey_ecdsa.x509.pem
rename to tests/testdata/testkey_ecdsa.x509.pem
diff --git a/testdata/testkey_sha256.x509.pem b/tests/testdata/testkey_sha256.x509.pem
similarity index 100%
rename from testdata/testkey_sha256.x509.pem
rename to tests/testdata/testkey_sha256.x509.pem
diff --git a/testdata/unsigned.zip b/tests/testdata/unsigned.zip
similarity index 100%
rename from testdata/unsigned.zip
rename to tests/testdata/unsigned.zip
Binary files differ
diff --git a/tests/asn1_decoder_test.cpp b/tests/unit/asn1_decoder_test.cpp
similarity index 100%
rename from tests/asn1_decoder_test.cpp
rename to tests/unit/asn1_decoder_test.cpp
diff --git a/uncrypt/uncrypt.cpp b/uncrypt/uncrypt.cpp
index de7e481..705744e 100644
--- a/uncrypt/uncrypt.cpp
+++ b/uncrypt/uncrypt.cpp
@@ -42,6 +42,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <libgen.h>
 #include <linux/fs.h>
 #include <stdarg.h>
 #include <stdio.h>
@@ -52,9 +53,13 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <memory>
+#include <vector>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <cutils/android_reboot.h>
 #include <cutils/properties.h>
@@ -63,59 +68,39 @@
 #define LOG_TAG "uncrypt"
 #include <log/log.h>
 
+#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;
     }
-    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<off64_t>(written), strerror(errno));
-            return -1;
-        }
-        written += wrote;
+    if (!android::base::WriteFully(wfd, buffer, size)) {
+        ALOGE("error writing offset %" PRId64 ": %s", offset, strerror(errno));
+        return -1;
     }
     return 0;
 }
 
-static void add_block_to_ranges(int** ranges, int* range_alloc, int* range_used, int new_block) {
-    // If the current block start is < 0, set the start to the new
-    // block.  (This only happens for the very first block of the very
-    // first range.)
-    if ((*ranges)[*range_used*2-2] < 0) {
-        (*ranges)[*range_used*2-2] = new_block;
-        (*ranges)[*range_used*2-1] = new_block;
-    }
-
-    if (new_block == (*ranges)[*range_used*2-1]) {
+static void add_block_to_ranges(std::vector<int>& ranges, int new_block) {
+    if (!ranges.empty() && new_block == ranges.back()) {
         // If the new block comes immediately after the current range,
         // all we have to do is extend the current range.
-        ++(*ranges)[*range_used*2-1];
+        ++ranges.back();
     } else {
         // We need to start a new range.
-
-        // If there isn't enough room in the array, we need to expand it.
-        if (*range_used >= *range_alloc) {
-            *range_alloc *= 2;
-            *ranges = reinterpret_cast<int*>(realloc(*ranges, *range_alloc * 2 * sizeof(int)));
-        }
-
-        ++*range_used;
-        (*ranges)[*range_used*2-2] = new_block;
-        (*ranges)[*range_used*2-1] = new_block+1;
+        ranges.push_back(new_block);
+        ranges.push_back(new_block + 1);
     }
 }
 
@@ -125,13 +110,13 @@
     // 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;
     }
 
@@ -168,77 +153,77 @@
 }
 
 // Parse uncrypt_file to find the update package name.
-static bool find_uncrypt_package(std::string& package_name)
-{
-    if (!android::base::ReadFileToString(uncrypt_file, &package_name)) {
-        ALOGE("failed to open \"%s\": %s\n", uncrypt_file.c_str(), strerror(errno));
+static bool find_uncrypt_package(const std::string& uncrypt_path_file, std::string* package_name) {
+    CHECK(package_name != nullptr);
+    std::string uncrypt_path;
+    if (!android::base::ReadFileToString(uncrypt_path_file, &uncrypt_path)) {
+        ALOGE("failed to open \"%s\": %s", uncrypt_path_file.c_str(), strerror(errno));
         return false;
     }
 
     // Remove the trailing '\n' if present.
-    package_name = android::base::Trim(package_name);
-
+    *package_name = android::base::Trim(uncrypt_path);
     return true;
 }
 
 static int produce_block_map(const char* path, const char* map_file, const char* blk_dev,
                              bool encrypted, int status_fd) {
-    int mapfd = open(map_file, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
-    if (mapfd == -1) {
-        ALOGE("failed to open %s\n", map_file);
+    std::string err;
+    if (!android::base::RemoveFileIfExists(map_file, &err)) {
+        ALOGE("failed to remove the existing map file %s: %s", map_file, err.c_str());
         return -1;
     }
-    std::unique_ptr<FILE, int(*)(FILE*)> mapf(fdopen(mapfd, "w"), fclose);
+    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;
+    }
 
     // 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<long>(sb.st_blksize));
+    ALOGI(" block size: %ld bytes", static_cast<long>(sb.st_blksize));
 
     int blocks = ((sb.st_size-1) / sb.st_blksize) + 1;
-    ALOGI("  file size: %" PRId64 " bytes, %d blocks\n", sb.st_size, blocks);
+    ALOGI("  file size: %" PRId64 " bytes, %d blocks", sb.st_size, blocks);
 
-    int range_alloc = 1;
-    int range_used = 1;
-    int* ranges = reinterpret_cast<int*>(malloc(range_alloc * 2 * sizeof(int)));
-    ranges[0] = -1;
-    ranges[1] = -1;
+    std::vector<int> ranges;
 
-    fprintf(mapf.get(), "%s\n%" PRId64 " %ld\n",
-            blk_dev, sb.st_size, static_cast<long>(sb.st_blksize));
+    std::string s = android::base::StringPrintf("%s\n%" PRId64 " %ld\n",
+                       blk_dev, sb.st_size, static_cast<long>(sb.st_blksize));
+    if (!android::base::WriteStringToFd(s, mapfd.get())) {
+        ALOGE("failed to write %s: %s", tmp_map_file.c_str(), strerror(errno));
+        return -1;
+    }
 
-    unsigned char* buffers[WINDOW_SIZE];
+    std::vector<std::vector<unsigned char>> buffers;
     if (encrypted) {
-        for (size_t i = 0; i < WINDOW_SIZE; ++i) {
-            buffers[i] = reinterpret_cast<unsigned char*>(malloc(sb.st_blksize));
-        }
+        buffers.resize(WINDOW_SIZE, std::vector<unsigned char>(sb.st_blksize));
     }
     int head_block = 0;
     int head = 0, tail = 0;
 
-    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", 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) {
-            ALOGE("failed to open fd for writing: %s\n", strerror(errno));
+        if (!wfd) {
+            ALOGE("failed to open fd for writing: %s", strerror(errno));
             return -1;
         }
     }
@@ -256,13 +241,13 @@
         if ((tail+1) % WINDOW_SIZE == head) {
             // write out head buffer
             int block = head_block;
-            if (ioctl(fd, FIBMAP, &block) != 0) {
-                ALOGE("failed to find block %d\n", head_block);
+            if (ioctl(fd.get(), FIBMAP, &block) != 0) {
+                ALOGE("failed to find block %d", head_block);
                 return -1;
             }
-            add_block_to_ranges(&ranges, &range_alloc, &range_used, block);
+            add_block_to_ranges(ranges, block);
             if (encrypted) {
-                if (write_at_offset(buffers[head], sb.st_blksize, wfd,
+                if (write_at_offset(buffers[head].data(), sb.st_blksize, wfd.get(),
                         static_cast<off64_t>(sb.st_blksize) * block) != 0) {
                     return -1;
                 }
@@ -273,17 +258,13 @@
 
         // read next block to tail
         if (encrypted) {
-            size_t so_far = 0;
-            while (so_far < static_cast<size_t>(sb.st_blksize) && pos < sb.st_size) {
-                ssize_t this_read =
-                        TEMP_FAILURE_RETRY(read(fd, buffers[tail] + so_far, sb.st_blksize - so_far));
-                if (this_read == -1) {
-                    ALOGE("failed to read: %s\n", strerror(errno));
-                    return -1;
-                }
-                so_far += this_read;
-                pos += this_read;
+            size_t to_read = static_cast<size_t>(
+                    std::min(static_cast<off64_t>(sb.st_blksize), sb.st_size - pos));
+            if (!android::base::ReadFully(fd.get(), buffers[tail].data(), to_read)) {
+                ALOGE("failed to read: %s", strerror(errno));
+                return -1;
             }
+            pos += to_read;
         } else {
             // If we're not encrypting; we don't need to actually read
             // anything, just skip pos forward as if we'd read a
@@ -296,13 +277,13 @@
     while (head != tail) {
         // write out head buffer
         int block = head_block;
-        if (ioctl(fd, FIBMAP, &block) != 0) {
-            ALOGE("failed to find block %d\n", head_block);
+        if (ioctl(fd.get(), FIBMAP, &block) != 0) {
+            ALOGE("failed to find block %d", head_block);
             return -1;
         }
-        add_block_to_ranges(&ranges, &range_alloc, &range_used, block);
+        add_block_to_ranges(ranges, block);
         if (encrypted) {
-            if (write_at_offset(buffers[head], sb.st_blksize, wfd,
+            if (write_at_offset(buffers[head].data(), sb.st_blksize, wfd.get(),
                     static_cast<off64_t>(sb.st_blksize) * block) != 0) {
                 return -1;
             }
@@ -311,54 +292,118 @@
         ++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 (fsync(mapfd) == -1) {
-        ALOGE("failed to fsync \"%s\": %s\n", map_file, strerror(errno));
+    if (!android::base::WriteStringToFd(
+            android::base::StringPrintf("%zu\n", ranges.size() / 2), mapfd.get())) {
+        ALOGE("failed to write %s: %s", tmp_map_file.c_str(), strerror(errno));
         return -1;
     }
-    if (encrypted) {
-        if (fsync(wfd) == -1) {
-            ALOGE("failed to fsync \"%s\": %s\n", blk_dev, strerror(errno));
+    for (size_t i = 0; i < ranges.size(); i += 2) {
+        if (!android::base::WriteStringToFd(
+                android::base::StringPrintf("%d %d\n", ranges[i], ranges[i+1]), mapfd.get())) {
+            ALOGE("failed to write %s: %s", tmp_map_file.c_str(), strerror(errno));
             return -1;
         }
     }
 
+    if (fsync(mapfd.get()) == -1) {
+        ALOGE("failed to fsync \"%s\": %s", tmp_map_file.c_str(), strerror(errno));
+        return -1;
+    }
+    if (close(mapfd.get() == -1)) {
+        ALOGE("failed to close %s: %s", tmp_map_file.c_str(), strerror(errno));
+        return -1;
+    }
+    mapfd = -1;
+
+    if (encrypted) {
+        if (fsync(wfd.get()) == -1) {
+            ALOGE("failed to fsync \"%s\": %s", blk_dev, strerror(errno));
+            return -1;
+        }
+        if (close(wfd.get()) == -1) {
+            ALOGE("failed to close %s: %s", blk_dev, strerror(errno));
+            return -1;
+        }
+        wfd = -1;
+    }
+
+    if (rename(tmp_map_file.c_str(), map_file) == -1) {
+        ALOGE("failed to rename %s to %s: %s", tmp_map_file.c_str(), map_file, strerror(errno));
+        return -1;
+    }
+    // Sync dir to make rename() result written to disk.
+    std::string file_name = map_file;
+    std::string dir_name = dirname(&file_name[0]);
+    unique_fd dfd(open(dir_name.c_str(), O_RDONLY | O_DIRECTORY));
+    if (!dfd) {
+        ALOGE("failed to open dir %s: %s", dir_name.c_str(), strerror(errno));
+        return -1;
+    }
+    if (fsync(dfd.get()) == -1) {
+        ALOGE("failed to fsync %s: %s", dir_name.c_str(), strerror(errno));
+        return -1;
+    }
+    if (close(dfd.get() == -1)) {
+        ALOGE("failed to close %s: %s", dir_name.c_str(), strerror(errno));
+        return -1;
+    }
+    dfd = -1;
     return 0;
 }
 
-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() {
@@ -370,7 +415,7 @@
     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);
 
@@ -397,8 +442,8 @@
     // 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
@@ -417,56 +462,113 @@
     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] [<transform_path> <map_file>]\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.
+    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;
+        }
+        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;
+}
+
+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 [<package_path> <map_file>]  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 (read_fstab() == NULL) {
-            return 1;
+        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();
         }
-        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);
-
-        std::string package;
-        const char* input_path;
-        const char* map_file;
-
+    } 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 5f4c479..b07c1da 100644
--- a/uncrypt/uncrypt.rc
+++ b/uncrypt/uncrypt.rc
@@ -7,3 +7,13 @@
     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
diff --git a/updater/Android.mk b/updater/Android.mk
index dcf4374..d7aa613 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -45,8 +45,8 @@
 endif
 
 LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UPDATER_LIBS) $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS)
-LOCAL_STATIC_LIBRARIES += libapplypatch libbase libedify libmtdutils libminzip libz
-LOCAL_STATIC_LIBRARIES += libmincrypt libbz
+LOCAL_STATIC_LIBRARIES += libapplypatch libbase libotafault libedify libmtdutils libminzip libz
+LOCAL_STATIC_LIBRARIES += libbz
 LOCAL_STATIC_LIBRARIES += libcutils liblog libc
 LOCAL_STATIC_LIBRARIES += libselinux
 tune2fs_static_libraries := \
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index c6daf7d..44de4e0 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -43,8 +43,9 @@
 #include "applypatch/applypatch.h"
 #include "edify/expr.h"
 #include "install.h"
-#include "mincrypt/sha.h"
+#include "openssl/sha.h"
 #include "minzip/Hash.h"
+#include "otafault/ota_io.h"
 #include "print_sha1.h"
 #include "unique_fd.h"
 #include "updater.h"
@@ -139,7 +140,7 @@
 static int read_all(int fd, uint8_t* data, size_t size) {
     size_t so_far = 0;
     while (so_far < size) {
-        ssize_t r = TEMP_FAILURE_RETRY(read(fd, data+so_far, size-so_far));
+        ssize_t r = TEMP_FAILURE_RETRY(ota_read(fd, data+so_far, size-so_far));
         if (r == -1) {
             fprintf(stderr, "read failed: %s\n", strerror(errno));
             return -1;
@@ -156,7 +157,7 @@
 static int write_all(int fd, const uint8_t* data, size_t size) {
     size_t written = 0;
     while (written < size) {
-        ssize_t w = TEMP_FAILURE_RETRY(write(fd, data+written, size-written));
+        ssize_t w = TEMP_FAILURE_RETRY(ota_write(fd, data+written, size-written));
         if (w == -1) {
             fprintf(stderr, "write failed: %s\n", strerror(errno));
             return -1;
@@ -407,10 +408,10 @@
 
 static int VerifyBlocks(const std::string& expected, const std::vector<uint8_t>& buffer,
         const size_t blocks, bool printerror) {
-    uint8_t digest[SHA_DIGEST_SIZE];
+    uint8_t digest[SHA_DIGEST_LENGTH];
     const uint8_t* data = buffer.data();
 
-    SHA_hash(data, blocks * BLOCKSIZE, digest);
+    SHA1(data, blocks * BLOCKSIZE, digest);
 
     std::string hexdigest = print_sha1(digest);
 
@@ -553,7 +554,7 @@
         return -1;
     }
 
-    int fd = TEMP_FAILURE_RETRY(open(fn.c_str(), O_RDONLY));
+    int fd = TEMP_FAILURE_RETRY(ota_open(fn.c_str(), O_RDONLY));
     unique_fd fd_holder(fd);
 
     if (fd == -1) {
@@ -610,7 +611,7 @@
 
     fprintf(stderr, " writing %d blocks to %s\n", blocks, cn.c_str());
 
-    int fd = TEMP_FAILURE_RETRY(open(fn.c_str(), O_WRONLY | O_CREAT | O_TRUNC, STASH_FILE_MODE));
+    int fd = TEMP_FAILURE_RETRY(ota_open(fn.c_str(), O_WRONLY | O_CREAT | O_TRUNC, STASH_FILE_MODE));
     unique_fd fd_holder(fd);
 
     if (fd == -1) {
@@ -622,7 +623,7 @@
         return -1;
     }
 
-    if (fsync(fd) == -1) {
+    if (ota_fsync(fd) == -1) {
         fprintf(stderr, "fsync \"%s\" failed: %s\n", fn.c_str(), strerror(errno));
         return -1;
     }
@@ -634,7 +635,7 @@
     }
 
     std::string dname = GetStashFileName(base, "", "");
-    int dfd = TEMP_FAILURE_RETRY(open(dname.c_str(), O_RDONLY | O_DIRECTORY));
+    int dfd = TEMP_FAILURE_RETRY(ota_open(dname.c_str(), O_RDONLY | O_DIRECTORY));
     unique_fd dfd_holder(dfd);
 
     if (dfd == -1) {
@@ -642,7 +643,7 @@
         return -1;
     }
 
-    if (fsync(dfd) == -1) {
+    if (ota_fsync(dfd) == -1) {
         fprintf(stderr, "fsync \"%s\" failed: %s\n", dname.c_str(), strerror(errno));
         return -1;
     }
@@ -662,10 +663,8 @@
     // Stash directory should be different for each partition to avoid conflicts
     // when updating multiple partitions at the same time, so we use the hash of
     // the block device name as the base directory
-    SHA_CTX ctx;
-    SHA_init(&ctx);
-    SHA_update(&ctx, blockdev, strlen(blockdev));
-    const uint8_t* digest = SHA_final(&ctx);
+    uint8_t digest[SHA_DIGEST_LENGTH];
+    SHA1(reinterpret_cast<const uint8_t*>(blockdev), strlen(blockdev), digest);
     base = print_sha1(digest);
 
     std::string dirname = GetStashFileName(base, "", "");
@@ -1348,7 +1347,7 @@
         return StringValue(strdup(""));
     }
 
-    params.fd = TEMP_FAILURE_RETRY(open(blockdev_filename->data, O_RDWR));
+    params.fd = TEMP_FAILURE_RETRY(ota_open(blockdev_filename->data, O_RDWR));
     unique_fd fd_holder(params.fd);
 
     if (params.fd == -1) {
@@ -1467,7 +1466,7 @@
         }
 
         if (params.canwrite) {
-            if (fsync(params.fd) == -1) {
+            if (ota_fsync(params.fd) == -1) {
                 fprintf(stderr, "fsync failed: %s\n", strerror(errno));
                 goto pbiudone;
             }
@@ -1492,7 +1491,7 @@
     rc = 0;
 
 pbiudone:
-    if (fsync(params.fd) == -1) {
+    if (ota_fsync(params.fd) == -1) {
         fprintf(stderr, "fsync failed: %s\n", strerror(errno));
     }
     // params.fd will be automatically closed because of the fd_holder above.
@@ -1616,7 +1615,7 @@
         return StringValue(strdup(""));
     }
 
-    int fd = open(blockdev_filename->data, O_RDWR);
+    int fd = ota_open(blockdev_filename->data, O_RDWR);
     unique_fd fd_holder(fd);
     if (fd < 0) {
         ErrorAbort(state, "open \"%s\" failed: %s", blockdev_filename->data, strerror(errno));
@@ -1627,7 +1626,7 @@
     parse_range(ranges->data, rs);
 
     SHA_CTX ctx;
-    SHA_init(&ctx);
+    SHA1_Init(&ctx);
 
     std::vector<uint8_t> buffer(BLOCKSIZE);
     for (size_t i = 0; i < rs.count; ++i) {
@@ -1643,10 +1642,11 @@
                 return StringValue(strdup(""));
             }
 
-            SHA_update(&ctx, buffer.data(), BLOCKSIZE);
+            SHA1_Update(&ctx, buffer.data(), BLOCKSIZE);
         }
     }
-    const uint8_t* digest = SHA_final(&ctx);
+    uint8_t digest[SHA_DIGEST_LENGTH];
+    SHA1_Final(digest, &ctx);
 
     return StringValue(strdup(print_sha1(digest).c_str()));
 }
@@ -1669,7 +1669,7 @@
         return StringValue(strdup(""));
     }
 
-    int fd = open(arg_filename->data, O_RDONLY);
+    int fd = ota_open(arg_filename->data, O_RDONLY);
     unique_fd fd_holder(fd);
     if (fd == -1) {
         ErrorAbort(state, "open \"%s\" failed: %s", arg_filename->data, strerror(errno));
diff --git a/updater/install.cpp b/updater/install.cpp
index b090869..413e147 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -34,6 +34,9 @@
 #include <linux/xattr.h>
 #include <inttypes.h>
 
+#include <memory>
+#include <vector>
+
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 #include <android-base/stringprintf.h>
@@ -44,10 +47,11 @@
 #include "cutils/misc.h"
 #include "cutils/properties.h"
 #include "edify/expr.h"
-#include "mincrypt/sha.h"
+#include "openssl/sha.h"
 #include "minzip/DirUtil.h"
 #include "mtdutils/mounts.h"
 #include "mtdutils/mtdutils.h"
+#include "otafault/ota_io.h"
 #include "updater.h"
 #include "install.h"
 #include "tune2fs.h"
@@ -91,10 +95,10 @@
 
 // Take a sha-1 digest and return it as a newly-allocated hex string.
 char* PrintSha1(const uint8_t* digest) {
-    char* buffer = reinterpret_cast<char*>(malloc(SHA_DIGEST_SIZE*2 + 1));
+    char* buffer = reinterpret_cast<char*>(malloc(SHA_DIGEST_LENGTH*2 + 1));
     const char* alphabet = "0123456789abcdef";
     size_t i;
-    for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
+    for (i = 0; i < SHA_DIGEST_LENGTH; ++i) {
         buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf];
         buffer[i*2+1] = alphabet[digest[i] & 0xf];
     }
@@ -439,8 +443,7 @@
     for (int i = 0; i < argc; ++i) {
         paths[i] = Evaluate(state, argv[i]);
         if (paths[i] == NULL) {
-            int j;
-            for (j = 0; j < i; ++i) {
+            for (int j = 0; j < i; ++j) {
                 free(paths[j]);
             }
             free(paths);
@@ -555,18 +558,18 @@
         }
 
         {
-            int fd = TEMP_FAILURE_RETRY(open(dest_path, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC,
+            int fd = TEMP_FAILURE_RETRY(ota_open(dest_path, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC,
                   S_IRUSR | S_IWUSR));
             if (fd == -1) {
                 printf("%s: can't open %s for write: %s\n", name, dest_path, strerror(errno));
                 goto done2;
             }
             success = mzExtractZipEntryToFile(za, entry, fd);
-            if (fsync(fd) == -1) {
+            if (ota_fsync(fd) == -1) {
                 printf("fsync of \"%s\" failed: %s\n", dest_path, strerror(errno));
                 success = false;
             }
-            if (close(fd) == -1) {
+            if (ota_close(fd) == -1) {
                 printf("close of \"%s\" failed: %s\n", dest_path, strerror(errno));
                 success = false;
             }
@@ -581,13 +584,13 @@
         // as the result.
 
         char* zip_path;
+        if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL;
+
         Value* v = reinterpret_cast<Value*>(malloc(sizeof(Value)));
         v->type = VAL_BLOB;
         v->size = -1;
         v->data = NULL;
 
-        if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL;
-
         ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
         const ZipEntry* entry = mzFindZipEntry(za, zip_path);
         if (entry == NULL) {
@@ -993,21 +996,21 @@
     }
 
     FILE* f;
-    f = fopen(filename, "rb");
+    f = ota_fopen(filename, "rb");
     if (f == NULL) {
         ErrorAbort(state, "%s: failed to open %s: %s", name, filename, strerror(errno));
         goto done;
     }
 
-    if (fread(buffer, 1, st.st_size, f) != static_cast<size_t>(st.st_size)) {
+    if (ota_fread(buffer, 1, st.st_size, f) != static_cast<size_t>(st.st_size)) {
         ErrorAbort(state, "%s: failed to read %lld bytes from %s",
                    name, (long long)st.st_size+1, filename);
-        fclose(f);
+        ota_fclose(f);
         goto done;
     }
     buffer[st.st_size] = '\0';
 
-    fclose(f);
+    ota_fclose(f);
 
     char* line;
     line = strtok(buffer, "\n");
@@ -1102,7 +1105,7 @@
     if (contents->type == VAL_STRING) {
         // we're given a filename as the contents
         char* filename = contents->data;
-        FILE* f = fopen(filename, "rb");
+        FILE* f = ota_fopen(filename, "rb");
         if (f == NULL) {
             printf("%s: can't open %s: %s\n", name, filename, strerror(errno));
             result = strdup("");
@@ -1112,12 +1115,12 @@
         success = true;
         char* buffer = reinterpret_cast<char*>(malloc(BUFSIZ));
         int read;
-        while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
+        while (success && (read = ota_fread(buffer, 1, BUFSIZ, f)) > 0) {
             int wrote = mtd_write_data(ctx, buffer, read);
             success = success && (wrote == read);
         }
         free(buffer);
-        fclose(f);
+        ota_fclose(f);
     } else {
         // we're given a blob as the contents
         ssize_t wrote = mtd_write_data(ctx, contents->data, contents->size);
@@ -1193,44 +1196,40 @@
     }
 
     int patchcount = (argc-4) / 2;
-    Value** patches = ReadValueVarArgs(state, argc-4, argv+4);
+    std::unique_ptr<Value*, decltype(&free)> arg_values(ReadValueVarArgs(state, argc-4, argv+4),
+                                                        free);
+    if (!arg_values) {
+        return nullptr;
+    }
+    std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> patch_shas;
+    std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> patches;
+    // Protect values by unique_ptrs first to get rid of memory leak.
+    for (int i = 0; i < patchcount * 2; i += 2) {
+        patch_shas.emplace_back(arg_values.get()[i], FreeValue);
+        patches.emplace_back(arg_values.get()[i+1], FreeValue);
+    }
 
-    int i;
-    for (i = 0; i < patchcount; ++i) {
-        if (patches[i*2]->type != VAL_STRING) {
+    for (int i = 0; i < patchcount; ++i) {
+        if (patch_shas[i]->type != VAL_STRING) {
             ErrorAbort(state, "%s(): sha-1 #%d is not string", name, i);
-            break;
+            return nullptr;
         }
-        if (patches[i*2+1]->type != VAL_BLOB) {
+        if (patches[i]->type != VAL_BLOB) {
             ErrorAbort(state, "%s(): patch #%d is not blob", name, i);
-            break;
+            return nullptr;
         }
     }
-    if (i != patchcount) {
-        for (i = 0; i < patchcount*2; ++i) {
-            FreeValue(patches[i]);
-        }
-        free(patches);
-        return NULL;
-    }
 
-    char** patch_sha_str = reinterpret_cast<char**>(malloc(patchcount * sizeof(char*)));
-    for (i = 0; i < patchcount; ++i) {
-        patch_sha_str[i] = patches[i*2]->data;
-        patches[i*2]->data = NULL;
-        FreeValue(patches[i*2]);
-        patches[i] = patches[i*2+1];
+    std::vector<char*> patch_sha_str;
+    std::vector<Value*> patch_ptrs;
+    for (int i = 0; i < patchcount; ++i) {
+        patch_sha_str.push_back(patch_shas[i]->data);
+        patch_ptrs.push_back(patches[i].get());
     }
 
     int result = applypatch(source_filename, target_filename,
                             target_sha1, target_size,
-                            patchcount, patch_sha_str, patches, NULL);
-
-    for (i = 0; i < patchcount; ++i) {
-        FreeValue(patches[i]);
-    }
-    free(patch_sha_str);
-    free(patches);
+                            patchcount, patch_sha_str.data(), patch_ptrs.data(), NULL);
 
     return StringValue(strdup(result == 0 ? "t" : ""));
 }
@@ -1349,24 +1348,27 @@
         return ErrorAbort(state, "%s() expects at least 1 arg", name);
     }
 
-    Value** args = ReadValueVarArgs(state, argc, argv);
-    if (args == NULL) {
-        return NULL;
+    std::unique_ptr<Value*, decltype(&free)> arg_values(ReadValueVarArgs(state, argc, argv), free);
+    if (arg_values == nullptr) {
+        return nullptr;
+    }
+    std::vector<std::unique_ptr<Value, decltype(&FreeValue)>> args;
+    for (int i = 0; i < argc; ++i) {
+        args.emplace_back(arg_values.get()[i], FreeValue);
     }
 
     if (args[0]->size < 0) {
         return StringValue(strdup(""));
     }
-    uint8_t digest[SHA_DIGEST_SIZE];
-    SHA_hash(args[0]->data, args[0]->size, digest);
-    FreeValue(args[0]);
+    uint8_t digest[SHA_DIGEST_LENGTH];
+    SHA1(reinterpret_cast<uint8_t*>(args[0]->data), args[0]->size, digest);
 
     if (argc == 1) {
         return StringValue(PrintSha1(digest));
     }
 
     int i;
-    uint8_t* arg_digest = reinterpret_cast<uint8_t*>(malloc(SHA_DIGEST_SIZE));
+    uint8_t arg_digest[SHA_DIGEST_LENGTH];
     for (i = 1; i < argc; ++i) {
         if (args[i]->type != VAL_STRING) {
             printf("%s(): arg %d is not a string; skipping",
@@ -1375,22 +1377,16 @@
             // Warn about bad args and skip them.
             printf("%s(): error parsing \"%s\" as sha-1; skipping",
                    name, args[i]->data);
-        } else if (memcmp(digest, arg_digest, SHA_DIGEST_SIZE) == 0) {
+        } else if (memcmp(digest, arg_digest, SHA_DIGEST_LENGTH) == 0) {
             break;
         }
-        FreeValue(args[i]);
     }
     if (i >= argc) {
         // Didn't match any of the hex strings; return false.
         return StringValue(strdup(""));
     }
-    // Found a match; free all the remaining arguments and return the
-    // matched one.
-    int j;
-    for (j = i+1; j < argc; ++j) {
-        FreeValue(args[j]);
-    }
-    return args[i];
+    // Found a match.
+    return args[i].release();
 }
 
 // Read a local file and return its contents (the Value* returned
@@ -1402,21 +1398,22 @@
     char* filename;
     if (ReadArgs(state, argv, 1, &filename) < 0) return NULL;
 
-    Value* v = reinterpret_cast<Value*>(malloc(sizeof(Value)));
+    Value* v = static_cast<Value*>(malloc(sizeof(Value)));
+    if (v == nullptr) {
+        return nullptr;
+    }
     v->type = VAL_BLOB;
+    v->size = -1;
+    v->data = nullptr;
 
     FileContents fc;
     if (LoadFileContents(filename, &fc) != 0) {
-        free(filename);
-        v->size = -1;
-        v->data = NULL;
-        free(fc.data);
-        return v;
+        v->data = static_cast<char*>(malloc(fc.data.size()));
+        if (v->data != nullptr) {
+            memcpy(v->data, fc.data.data(), fc.data.size());
+            v->size = fc.data.size();
+        }
     }
-
-    v->size = fc.size;
-    v->data = (char*)fc.data;
-
     free(filename);
     return v;
 }
@@ -1443,10 +1440,10 @@
 
     // zero out the 'command' field of the bootloader message.
     memset(buffer, 0, sizeof(((struct bootloader_message*)0)->command));
-    FILE* f = fopen(filename, "r+b");
+    FILE* f = ota_fopen(filename, "r+b");
     fseek(f, offsetof(struct bootloader_message, command), SEEK_SET);
-    fwrite(buffer, sizeof(((struct bootloader_message*)0)->command), 1, f);
-    fclose(f);
+    ota_fwrite(buffer, sizeof(((struct bootloader_message*)0)->command), 1, f);
+    ota_fclose(f);
     free(filename);
 
     strcpy(buffer, "reboot,");
@@ -1485,7 +1482,7 @@
     // bootloader message that the main recovery uses to save its
     // arguments in case of the device restarting midway through
     // package installation.
-    FILE* f = fopen(filename, "r+b");
+    FILE* f = ota_fopen(filename, "r+b");
     fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET);
     int to_write = strlen(stagestr)+1;
     int max_size = sizeof(((struct bootloader_message*)0)->stage);
@@ -1493,8 +1490,8 @@
         to_write = max_size;
         stagestr[max_size-1] = 0;
     }
-    fwrite(stagestr, to_write, 1, f);
-    fclose(f);
+    ota_fwrite(stagestr, to_write, 1, f);
+    ota_fclose(f);
 
     free(stagestr);
     return StringValue(filename);
@@ -1511,10 +1508,10 @@
     if (ReadArgs(state, argv, 1, &filename) < 0) return NULL;
 
     char buffer[sizeof(((struct bootloader_message*)0)->stage)];
-    FILE* f = fopen(filename, "rb");
+    FILE* f = ota_fopen(filename, "rb");
     fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET);
-    fread(buffer, sizeof(buffer), 1, f);
-    fclose(f);
+    ota_fread(buffer, sizeof(buffer), 1, f);
+    ota_fclose(f);
     buffer[sizeof(buffer)-1] = '\0';
 
     return StringValue(strdup(buffer));
@@ -1531,13 +1528,13 @@
 
     size_t len;
     android::base::ParseUint(len_str, &len);
-    int fd = open(filename, O_WRONLY, 0644);
+    int fd = ota_open(filename, O_WRONLY, 0644);
     int success = wipe_block_device(fd, len);
 
     free(filename);
     free(len_str);
 
-    close(fd);
+    ota_close(fd);
 
     return StringValue(strdup(success ? "t" : ""));
 }
diff --git a/verifier.cpp b/verifier.cpp
index 61e5adf..9a2d60c 100644
--- a/verifier.cpp
+++ b/verifier.cpp
@@ -113,7 +113,7 @@
 // or no key matches the signature).
 
 int verify_file(unsigned char* addr, size_t length,
-                const Certificate* pKeys, unsigned int numKeys) {
+                const std::vector<Certificate>& keys) {
     ui->SetProgress(0.0);
 
     // An archive with a whole-file signature will end in six bytes:
@@ -176,8 +176,7 @@
         return VERIFY_FAILURE;
     }
 
-    size_t i;
-    for (i = 4; i < eocd_size-3; ++i) {
+    for (size_t i = 4; i < eocd_size-3; ++i) {
         if (eocd[i  ] == 0x50 && eocd[i+1] == 0x4b &&
             eocd[i+2] == 0x05 && eocd[i+3] == 0x06) {
             // if the sequence $50 $4b $05 $06 appears anywhere after
@@ -193,8 +192,8 @@
 
     bool need_sha1 = false;
     bool need_sha256 = false;
-    for (i = 0; i < numKeys; ++i) {
-        switch (pKeys[i].hash_len) {
+    for (const auto& key : keys) {
+        switch (key.hash_len) {
             case SHA_DIGEST_SIZE: need_sha1 = true; break;
             case SHA256_DIGEST_SIZE: need_sha256 = true; break;
         }
@@ -225,7 +224,7 @@
     const uint8_t* sha1 = SHA_final(&sha1_ctx);
     const uint8_t* sha256 = SHA256_final(&sha256_ctx);
 
-    uint8_t* sig_der = NULL;
+    uint8_t* sig_der = nullptr;
     size_t sig_der_length = 0;
 
     size_t signature_size = signature_start - FOOTER_SIZE;
@@ -240,9 +239,10 @@
      * any key can match, we need to try each before determining a verification
      * failure has happened.
      */
-    for (i = 0; i < numKeys; ++i) {
+    size_t i = 0;
+    for (const auto& key : keys) {
         const uint8_t* hash;
-        switch (pKeys[i].hash_len) {
+        switch (key.hash_len) {
             case SHA_DIGEST_SIZE: hash = sha1; break;
             case SHA256_DIGEST_SIZE: hash = sha256; break;
             default: continue;
@@ -250,15 +250,15 @@
 
         // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that
         // the signing tool appends after the signature itself.
-        if (pKeys[i].key_type == Certificate::RSA) {
+        if (key.key_type == Certificate::RSA) {
             if (sig_der_length < RSANUMBYTES) {
                 // "signature" block isn't big enough to contain an RSA block.
                 LOGI("signature is too short for RSA key %zu\n", i);
                 continue;
             }
 
-            if (!RSA_verify(pKeys[i].rsa, sig_der, RSANUMBYTES,
-                            hash, pKeys[i].hash_len)) {
+            if (!RSA_verify(key.rsa.get(), sig_der, RSANUMBYTES,
+                            hash, key.hash_len)) {
                 LOGI("failed to verify against RSA key %zu\n", i);
                 continue;
             }
@@ -266,8 +266,8 @@
             LOGI("whole-file signature verified against RSA key %zu\n", i);
             free(sig_der);
             return VERIFY_SUCCESS;
-        } else if (pKeys[i].key_type == Certificate::EC
-                && pKeys[i].hash_len == SHA256_DIGEST_SIZE) {
+        } else if (key.key_type == Certificate::EC
+                && key.hash_len == SHA256_DIGEST_SIZE) {
             p256_int r, s;
             if (!dsa_sig_unpack(sig_der, sig_der_length, &r, &s)) {
                 LOGI("Not a DSA signature block for EC key %zu\n", i);
@@ -276,7 +276,7 @@
 
             p256_int p256_hash;
             p256_from_bin(hash, &p256_hash);
-            if (!p256_ecdsa_verify(&(pKeys[i].ec->x), &(pKeys[i].ec->y),
+            if (!p256_ecdsa_verify(&(key.ec->x), &(key.ec->y),
                                    &p256_hash, &r, &s)) {
                 LOGI("failed to verify against EC key %zu\n", i);
                 continue;
@@ -286,8 +286,9 @@
             free(sig_der);
             return VERIFY_SUCCESS;
         } else {
-            LOGI("Unknown key type %d\n", pKeys[i].key_type);
+            LOGI("Unknown key type %d\n", key.key_type);
         }
+        i++;
     }
     free(sig_der);
     LOGE("failed to verify whole-file signature\n");
@@ -323,140 +324,122 @@
 //       4: 2048-bit RSA key with e=65537 and SHA-256 hash
 //       5: 256-bit EC key using the NIST P-256 curve parameters and SHA-256 hash
 //
-// Returns NULL if the file failed to parse, or if it contain zero keys.
-Certificate*
-load_keys(const char* filename, int* numKeys) {
-    Certificate* out = NULL;
-    *numKeys = 0;
-
-    FILE* f = fopen(filename, "r");
-    if (f == NULL) {
+// Returns true on success, and appends the found keys (at least one) to certs.
+// Otherwise returns false if the file failed to parse, or if it contains zero
+// keys. The contents in certs would be unspecified on failure.
+bool load_keys(const char* filename, std::vector<Certificate>& certs) {
+    std::unique_ptr<FILE, decltype(&fclose)> f(fopen(filename, "r"), fclose);
+    if (!f) {
         LOGE("opening %s: %s\n", filename, strerror(errno));
-        goto exit;
+        return false;
     }
 
-    {
-        int i;
-        bool done = false;
-        while (!done) {
-            ++*numKeys;
-            out = (Certificate*)realloc(out, *numKeys * sizeof(Certificate));
-            Certificate* cert = out + (*numKeys - 1);
-            memset(cert, '\0', sizeof(Certificate));
+    while (true) {
+        certs.emplace_back(0, Certificate::RSA, nullptr, nullptr);
+        Certificate& cert = certs.back();
 
-            char start_char;
-            if (fscanf(f, " %c", &start_char) != 1) goto exit;
-            if (start_char == '{') {
-                // a version 1 key has no version specifier.
-                cert->key_type = Certificate::RSA;
-                cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
-                cert->rsa->exponent = 3;
-                cert->hash_len = SHA_DIGEST_SIZE;
-            } else if (start_char == 'v') {
-                int version;
-                if (fscanf(f, "%d {", &version) != 1) goto exit;
-                switch (version) {
-                    case 2:
-                        cert->key_type = Certificate::RSA;
-                        cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
-                        cert->rsa->exponent = 65537;
-                        cert->hash_len = SHA_DIGEST_SIZE;
-                        break;
-                    case 3:
-                        cert->key_type = Certificate::RSA;
-                        cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
-                        cert->rsa->exponent = 3;
-                        cert->hash_len = SHA256_DIGEST_SIZE;
-                        break;
-                    case 4:
-                        cert->key_type = Certificate::RSA;
-                        cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
-                        cert->rsa->exponent = 65537;
-                        cert->hash_len = SHA256_DIGEST_SIZE;
-                        break;
-                    case 5:
-                        cert->key_type = Certificate::EC;
-                        cert->ec = (ECPublicKey*)calloc(1, sizeof(ECPublicKey));
-                        cert->hash_len = SHA256_DIGEST_SIZE;
-                        break;
-                    default:
-                        goto exit;
-                }
+        char start_char;
+        if (fscanf(f.get(), " %c", &start_char) != 1) return false;
+        if (start_char == '{') {
+            // a version 1 key has no version specifier.
+            cert.key_type = Certificate::RSA;
+            cert.rsa = std::unique_ptr<RSAPublicKey>(new RSAPublicKey);
+            cert.rsa->exponent = 3;
+            cert.hash_len = SHA_DIGEST_SIZE;
+        } else if (start_char == 'v') {
+            int version;
+            if (fscanf(f.get(), "%d {", &version) != 1) return false;
+            switch (version) {
+                case 2:
+                    cert.key_type = Certificate::RSA;
+                    cert.rsa = std::unique_ptr<RSAPublicKey>(new RSAPublicKey);
+                    cert.rsa->exponent = 65537;
+                    cert.hash_len = SHA_DIGEST_SIZE;
+                    break;
+                case 3:
+                    cert.key_type = Certificate::RSA;
+                    cert.rsa = std::unique_ptr<RSAPublicKey>(new RSAPublicKey);
+                    cert.rsa->exponent = 3;
+                    cert.hash_len = SHA256_DIGEST_SIZE;
+                    break;
+                case 4:
+                    cert.key_type = Certificate::RSA;
+                    cert.rsa = std::unique_ptr<RSAPublicKey>(new RSAPublicKey);
+                    cert.rsa->exponent = 65537;
+                    cert.hash_len = SHA256_DIGEST_SIZE;
+                    break;
+                case 5:
+                    cert.key_type = Certificate::EC;
+                    cert.ec = std::unique_ptr<ECPublicKey>(new ECPublicKey);
+                    cert.hash_len = SHA256_DIGEST_SIZE;
+                    break;
+                default:
+                    return false;
             }
+        }
 
-            if (cert->key_type == Certificate::RSA) {
-                RSAPublicKey* key = cert->rsa;
-                if (fscanf(f, " %i , 0x%x , { %u",
-                           &(key->len), &(key->n0inv), &(key->n[0])) != 3) {
-                    goto exit;
-                }
-                if (key->len != RSANUMWORDS) {
-                    LOGE("key length (%d) does not match expected size\n", key->len);
-                    goto exit;
-                }
-                for (i = 1; i < key->len; ++i) {
-                    if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit;
-                }
-                if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit;
-                for (i = 1; i < key->len; ++i) {
-                    if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit;
-                }
-                fscanf(f, " } } ");
-
-                LOGI("read key e=%d hash=%d\n", key->exponent, cert->hash_len);
-            } else if (cert->key_type == Certificate::EC) {
-                ECPublicKey* key = cert->ec;
-                int key_len;
-                unsigned int byte;
-                uint8_t x_bytes[P256_NBYTES];
-                uint8_t y_bytes[P256_NBYTES];
-                if (fscanf(f, " %i , { %u", &key_len, &byte) != 2) goto exit;
-                if (key_len != P256_NBYTES) {
-                    LOGE("Key length (%d) does not match expected size %d\n", key_len, P256_NBYTES);
-                    goto exit;
-                }
-                x_bytes[P256_NBYTES - 1] = byte;
-                for (i = P256_NBYTES - 2; i >= 0; --i) {
-                    if (fscanf(f, " , %u", &byte) != 1) goto exit;
-                    x_bytes[i] = byte;
-                }
-                if (fscanf(f, " } , { %u", &byte) != 1) goto exit;
-                y_bytes[P256_NBYTES - 1] = byte;
-                for (i = P256_NBYTES - 2; i >= 0; --i) {
-                    if (fscanf(f, " , %u", &byte) != 1) goto exit;
-                    y_bytes[i] = byte;
-                }
-                fscanf(f, " } } ");
-                p256_from_bin(x_bytes, &key->x);
-                p256_from_bin(y_bytes, &key->y);
-            } else {
-                LOGE("Unknown key type %d\n", cert->key_type);
-                goto exit;
+        if (cert.key_type == Certificate::RSA) {
+            RSAPublicKey* key = cert.rsa.get();
+            if (fscanf(f.get(), " %i , 0x%x , { %u", &(key->len), &(key->n0inv),
+                    &(key->n[0])) != 3) {
+                return false;
             }
-
-            // if the line ends in a comma, this file has more keys.
-            switch (fgetc(f)) {
-            case ',':
-                // more keys to come.
-                break;
-
-            case EOF:
-                done = true;
-                break;
-
-            default:
-                LOGE("unexpected character between keys\n");
-                goto exit;
+            if (key->len != RSANUMWORDS) {
+                LOGE("key length (%d) does not match expected size\n", key->len);
+                return false;
             }
+            for (int i = 1; i < key->len; ++i) {
+                if (fscanf(f.get(), " , %u", &(key->n[i])) != 1) return false;
+            }
+            if (fscanf(f.get(), " } , { %u", &(key->rr[0])) != 1) return false;
+            for (int i = 1; i < key->len; ++i) {
+                if (fscanf(f.get(), " , %u", &(key->rr[i])) != 1) return false;
+            }
+            fscanf(f.get(), " } } ");
+
+            LOGI("read key e=%d hash=%d\n", key->exponent, cert.hash_len);
+        } else if (cert.key_type == Certificate::EC) {
+            ECPublicKey* key = cert.ec.get();
+            int key_len;
+            unsigned int byte;
+            uint8_t x_bytes[P256_NBYTES];
+            uint8_t y_bytes[P256_NBYTES];
+            if (fscanf(f.get(), " %i , { %u", &key_len, &byte) != 2) return false;
+            if (key_len != P256_NBYTES) {
+                LOGE("Key length (%d) does not match expected size %d\n", key_len, P256_NBYTES);
+                return false;
+            }
+            x_bytes[P256_NBYTES - 1] = byte;
+            for (int i = P256_NBYTES - 2; i >= 0; --i) {
+                if (fscanf(f.get(), " , %u", &byte) != 1) return false;
+                x_bytes[i] = byte;
+            }
+            if (fscanf(f.get(), " } , { %u", &byte) != 1) return false;
+            y_bytes[P256_NBYTES - 1] = byte;
+            for (int i = P256_NBYTES - 2; i >= 0; --i) {
+                if (fscanf(f.get(), " , %u", &byte) != 1) return false;
+                y_bytes[i] = byte;
+            }
+            fscanf(f.get(), " } } ");
+            p256_from_bin(x_bytes, &key->x);
+            p256_from_bin(y_bytes, &key->y);
+        } else {
+            LOGE("Unknown key type %d\n", cert.key_type);
+            return false;
+        }
+
+        // if the line ends in a comma, this file has more keys.
+        int ch = fgetc(f.get());
+        if (ch == ',') {
+            // more keys to come.
+            continue;
+        } else if (ch == EOF) {
+            break;
+        } else {
+            LOGE("unexpected character between keys\n");
+            return false;
         }
     }
 
-    fclose(f);
-    return out;
-
-exit:
-    if (f) fclose(f);
-    free(out);
-    *numKeys = 0;
-    return NULL;
+    return true;
 }
diff --git a/verifier.h b/verifier.h
index 15f8d98..4eafc75 100644
--- a/verifier.h
+++ b/verifier.h
@@ -17,6 +17,9 @@
 #ifndef _RECOVERY_VERIFIER_H
 #define _RECOVERY_VERIFIER_H
 
+#include <memory>
+#include <vector>
+
 #include "mincrypt/p256.h"
 #include "mincrypt/rsa.h"
 
@@ -25,17 +28,25 @@
     p256_int y;
 } ECPublicKey;
 
-typedef struct {
+struct Certificate {
     typedef enum {
         RSA,
         EC,
     } KeyType;
 
+    Certificate(int hash_len_, KeyType key_type_,
+            std::unique_ptr<RSAPublicKey>&& rsa_,
+            std::unique_ptr<ECPublicKey>&& ec_) :
+        hash_len(hash_len_),
+        key_type(key_type_),
+        rsa(std::move(rsa_)),
+        ec(std::move(ec_)) { }
+
     int hash_len;  // SHA_DIGEST_SIZE (SHA-1) or SHA256_DIGEST_SIZE (SHA-256)
     KeyType key_type;
-    RSAPublicKey* rsa;
-    ECPublicKey* ec;
-} Certificate;
+    std::unique_ptr<RSAPublicKey> rsa;
+    std::unique_ptr<ECPublicKey> ec;
+};
 
 /* addr and length define a an update package file that has been
  * loaded (or mmap'ed, or whatever) into memory.  Verify that the file
@@ -43,9 +54,9 @@
  * one of the constants below.
  */
 int verify_file(unsigned char* addr, size_t length,
-                const Certificate *pKeys, unsigned int numKeys);
+                const std::vector<Certificate>& keys);
 
-Certificate* load_keys(const char* filename, int* numKeys);
+bool load_keys(const char* filename, std::vector<Certificate>& certs);
 
 #define VERIFY_SUCCESS        0
 #define VERIFY_FAILURE        1
diff --git a/verifier_test.cpp b/verifier_test.cpp
deleted file mode 100644
index 21633dc..0000000
--- a/verifier_test.cpp
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include "common.h"
-#include "verifier.h"
-#include "ui.h"
-#include "mincrypt/sha.h"
-#include "mincrypt/sha256.h"
-#include "minzip/SysUtil.h"
-
-// This is build/target/product/security/testkey.x509.pem after being
-// dumped out by dumpkey.jar.
-RSAPublicKey test_key =
-    { 64, 0xc926ad21,
-      { 0x6afee91fu, 0x7fa31d5bu, 0x38a0b217u, 0x99df9baeu,
-        0xfe72991du, 0x727d3c04u, 0x20943f99u, 0xd08e7826u,
-        0x69e7c8a2u, 0xdeeccc8eu, 0x6b9af76fu, 0x553311c4u,
-        0x07b9e247u, 0x54c8bbcau, 0x6a540d81u, 0x48dbf567u,
-        0x98c92877u, 0x134fbfdeu, 0x01b32564u, 0x24581948u,
-        0x6cddc3b8u, 0x0cd444dau, 0xfe0381ccu, 0xf15818dfu,
-        0xc06e6d42u, 0x2e2f6412u, 0x093a6737u, 0x94d83b31u,
-        0xa466c87au, 0xb3f284a0u, 0xa694ec2cu, 0x053359e6u,
-        0x9717ee6au, 0x0732e080u, 0x220d5008u, 0xdc4af350u,
-        0x93d0a7c3u, 0xe330c9eau, 0xcac3da1eu, 0x8ebecf8fu,
-        0xc2be387fu, 0x38a14e89u, 0x211586f0u, 0x18b846f5u,
-        0x43be4c72u, 0xb578c204u, 0x1bbfb230u, 0xf1e267a8u,
-        0xa2d3e656u, 0x64b8e4feu, 0xe7e83d4bu, 0x3e77a943u,
-        0x3559ffd9u, 0x0ebb0f99u, 0x0aa76ce6u, 0xd3786ea7u,
-        0xbca8cd6bu, 0x068ca8e8u, 0xeb1de2ffu, 0x3e3ecd6cu,
-        0xe0d9d825u, 0xb1edc762u, 0xdec60b24u, 0xd6931904u},
-      { 0xccdcb989u, 0xe19281f9u, 0xa6e80accu, 0xb7f40560u,
-        0x0efb0bccu, 0x7f12b0bbu, 0x1e90531au, 0x136d95d0u,
-        0x9e660665u, 0x7d54918fu, 0xe3b93ea2u, 0x2f415d10u,
-        0x3d2df6e6u, 0x7a627ecfu, 0xa6f22d70u, 0xb995907au,
-        0x09de16b2u, 0xfeb8bd61u, 0xf24ec294u, 0x716a427fu,
-        0x2e12046fu, 0xeaf3d56au, 0xd9b873adu, 0x0ced340bu,
-        0xbc9cec09u, 0x73c65903u, 0xee39ce9bu, 0x3eede25au,
-        0x397633b7u, 0x2583c165u, 0x8514f97du, 0xe9166510u,
-        0x0b6fae99u, 0xa47139fdu, 0xdb8352f0u, 0xb2ad7f2cu,
-        0xa11552e2u, 0xd4d490a7u, 0xe11e8568u, 0xe9e484dau,
-        0xd3ef8449u, 0xa47055dau, 0x4edd9557u, 0x03a78ba1u,
-        0x770e130du, 0x16762facu, 0x0cbdfcc4u, 0xf3070540u,
-        0x008b6515u, 0x60e7e1b7u, 0xa72cf7f9u, 0xaff86e39u,
-        0x4296faadu, 0xfc90430eu, 0x6cc8f377u, 0xb398fd43u,
-        0x423c5997u, 0x991d59c4u, 0x6464bf73u, 0x96431575u,
-        0x15e3d207u, 0x30532a7au, 0x8c4be618u, 0x460a4d76u },
-      3
-    };
-
-RSAPublicKey test_f4_key =
-    { 64, 0xc9bd1f21,
-      { 0x1178db1fu, 0xbf5d0e55u, 0x3393a165u, 0x0ef4c287u,
-        0xbc472a4au, 0x383fc5a1u, 0x4a13b7d2u, 0xb1ff2ac3u,
-        0xaf66b4d9u, 0x9280acefu, 0xa2165bdbu, 0x6a4d6e5cu,
-        0x08ea676bu, 0xb7ac70c7u, 0xcd158139u, 0xa635ccfeu,
-        0xa46ab8a8u, 0x445a3e8bu, 0xdc81d9bbu, 0x91ce1a20u,
-        0x68021cdeu, 0x4516eda9u, 0x8d43c30cu, 0xed1eff14u,
-        0xca387e4cu, 0x58adc233u, 0x4657ab27u, 0xa95b521eu,
-        0xdfc0e30cu, 0x394d64a1u, 0xc6b321a1u, 0x2ca22cb8u,
-        0xb1892d5cu, 0x5d605f3eu, 0x6025483cu, 0x9afd5181u,
-        0x6e1a7105u, 0x03010593u, 0x70acd304u, 0xab957cbfu,
-        0x8844abbbu, 0x53846837u, 0x24e98a43u, 0x2ba060c1u,
-        0x8b88b88eu, 0x44eea405u, 0xb259fc41u, 0x0907ad9cu,
-        0x13003adau, 0xcf79634eu, 0x7d314ec9u, 0xfbbe4c2bu,
-        0xd84d0823u, 0xfd30fd88u, 0x68d8a909u, 0xfb4572d9u,
-        0xa21301c2u, 0xd00a4785u, 0x6862b50cu, 0xcfe49796u,
-        0xdaacbd83u, 0xfb620906u, 0xdf71e0ccu, 0xbbc5b030u },
-      { 0x69a82189u, 0x1a8b22f4u, 0xcf49207bu, 0x68cc056au,
-        0xb206b7d2u, 0x1d449bbdu, 0xe9d342f2u, 0x29daea58u,
-        0xb19d011au, 0xc62f15e4u, 0x9452697au, 0xb62bb87eu,
-        0x60f95cc2u, 0x279ebb2du, 0x17c1efd8u, 0xec47558bu,
-        0xc81334d1u, 0x88fe7601u, 0x79992eb1u, 0xb4555615u,
-        0x2022ac8cu, 0xc79a4b8cu, 0xb288b034u, 0xd6b942f0u,
-        0x0caa32fbu, 0xa065ba51u, 0x4de9f154u, 0x29f64f6cu,
-        0x7910af5eu, 0x3ed4636au, 0xe4c81911u, 0x9183f37du,
-        0x5811e1c4u, 0x29c7a58cu, 0x9715d4d3u, 0xc7e2dce3u,
-        0x140972ebu, 0xf4c8a69eu, 0xa104d424u, 0x5dabbdfbu,
-        0x41cb4c6bu, 0xd7f44717u, 0x61785ff7u, 0x5e0bc273u,
-        0x36426c70u, 0x2aa6f08eu, 0x083badbfu, 0x3cab941bu,
-        0x8871da23u, 0x1ab3dbaeu, 0x7115a21du, 0xf5aa0965u,
-        0xf766f562u, 0x7f110225u, 0x86d96a04u, 0xc50a120eu,
-        0x3a751ca3u, 0xc21aa186u, 0xba7359d0u, 0x3ff2b257u,
-        0xd116e8bbu, 0xfc1318c0u, 0x070e5b1du, 0x83b759a6u },
-      65537
-    };
-
-ECPublicKey test_ec_key =
-    {
-       {
-         {0xd656fa24u, 0x931416cau, 0x1c0278c6u, 0x174ebe4cu,
-          0x6018236au, 0x45ba1656u, 0xe8c05d84u, 0x670ed500u}
-      },
-      {
-        {0x0d179adeu, 0x4c16827du, 0x9f8cb992u, 0x8f69ff8au,
-         0x481b1020u, 0x798d91afu, 0x184db8e9u, 0xb5848dd9u}
-      }
-    };
-
-RecoveryUI* ui = NULL;
-
-// verifier expects to find a UI object; we provide one that does
-// nothing but print.
-class FakeUI : public RecoveryUI {
-    void Init() { }
-    void SetStage(int, int) { }
-    void SetLocale(const char*) { }
-    void SetBackground(Icon icon) { }
-
-    void SetProgressType(ProgressType determinate) { }
-    void ShowProgress(float portion, float seconds) { }
-    void SetProgress(float fraction) { }
-
-    void ShowText(bool visible) { }
-    bool IsTextVisible() { return false; }
-    bool WasTextEverVisible() { return false; }
-    void Print(const char* fmt, ...) {
-        va_list ap;
-        va_start(ap, fmt);
-        vfprintf(stderr, fmt, ap);
-        va_end(ap);
-    }
-    void PrintOnScreenOnly(const char* fmt, ...) {
-        va_list ap;
-        va_start(ap, fmt);
-        vfprintf(stderr, fmt, ap);
-        va_end(ap);
-    }
-    void ShowFile(const char*) { }
-
-    void StartMenu(const char* const * headers, const char* const * items,
-                           int initial_selection) { }
-    int SelectMenu(int sel) { return 0; }
-    void EndMenu() { }
-};
-
-void
-ui_print(const char* format, ...) {
-    va_list ap;
-    va_start(ap, format);
-    vfprintf(stdout, format, ap);
-    va_end(ap);
-}
-
-static Certificate* add_certificate(Certificate** certsp, int* num_keys,
-        Certificate::KeyType key_type) {
-    int i = *num_keys;
-    *num_keys = *num_keys + 1;
-    *certsp = (Certificate*) realloc(*certsp, *num_keys * sizeof(Certificate));
-    Certificate* certs = *certsp;
-    certs[i].rsa = NULL;
-    certs[i].ec = NULL;
-    certs[i].key_type = key_type;
-    certs[i].hash_len = SHA_DIGEST_SIZE;
-    return &certs[i];
-}
-
-int main(int argc, char **argv) {
-    if (argc < 2) {
-        fprintf(stderr, "Usage: %s [-sha256] [-ec | -f4 | -file <keys>] <package>\n", argv[0]);
-        return 2;
-    }
-    Certificate* certs = NULL;
-    int num_keys = 0;
-
-    int argn = 1;
-    while (argn < argc) {
-        if (strcmp(argv[argn], "-sha256") == 0) {
-            if (num_keys == 0) {
-                fprintf(stderr, "May only specify -sha256 after key type\n");
-                return 2;
-            }
-            ++argn;
-            Certificate* cert = &certs[num_keys - 1];
-            cert->hash_len = SHA256_DIGEST_SIZE;
-        } else if (strcmp(argv[argn], "-ec") == 0) {
-            ++argn;
-            Certificate* cert = add_certificate(&certs, &num_keys, Certificate::EC);
-            cert->ec = &test_ec_key;
-        } else if (strcmp(argv[argn], "-e3") == 0) {
-            ++argn;
-            Certificate* cert = add_certificate(&certs, &num_keys, Certificate::RSA);
-            cert->rsa = &test_key;
-        } else if (strcmp(argv[argn], "-f4") == 0) {
-            ++argn;
-            Certificate* cert = add_certificate(&certs, &num_keys, Certificate::RSA);
-            cert->rsa = &test_f4_key;
-        } else if (strcmp(argv[argn], "-file") == 0) {
-            if (certs != NULL) {
-                fprintf(stderr, "Cannot specify -file with other certs specified\n");
-                return 2;
-            }
-            ++argn;
-            certs = load_keys(argv[argn], &num_keys);
-            ++argn;
-        } else if (argv[argn][0] == '-') {
-            fprintf(stderr, "Unknown argument %s\n", argv[argn]);
-            return 2;
-        } else {
-            break;
-        }
-    }
-
-    if (argn == argc) {
-        fprintf(stderr, "Must specify package to verify\n");
-        return 2;
-    }
-
-    if (num_keys == 0) {
-        certs = (Certificate*) calloc(1, sizeof(Certificate));
-        if (certs == NULL) {
-            fprintf(stderr, "Failure allocating memory for default certificate\n");
-            return 1;
-        }
-        certs->key_type = Certificate::RSA;
-        certs->rsa = &test_key;
-        certs->ec = NULL;
-        certs->hash_len = SHA_DIGEST_SIZE;
-        num_keys = 1;
-    }
-
-    ui = new FakeUI();
-
-    MemMapping map;
-    if (sysMapFile(argv[argn], &map) != 0) {
-        fprintf(stderr, "failed to mmap %s: %s\n", argv[argn], strerror(errno));
-        return 4;
-    }
-
-    int result = verify_file(map.addr, map.length, certs, num_keys);
-    if (result == VERIFY_SUCCESS) {
-        printf("VERIFIED\n");
-        return 0;
-    } else if (result == VERIFY_FAILURE) {
-        printf("NOT VERIFIED\n");
-        return 1;
-    } else {
-        printf("bad return value\n");
-        return 3;
-    }
-}
diff --git a/verifier_test.sh b/verifier_test.sh
deleted file mode 100755
index 4761cef..0000000
--- a/verifier_test.sh
+++ /dev/null
@@ -1,121 +0,0 @@
-#!/bin/bash
-#
-# A test suite for recovery's package signature verifier.  Run in a
-# client where you have done envsetup, lunch, etc.
-#
-# TODO: find some way to get this run regularly along with the rest of
-# the tests.
-
-EMULATOR_PORT=5580
-DATA_DIR=$ANDROID_BUILD_TOP/bootable/recovery/testdata
-
-WORK_DIR=/data/local/tmp
-
-# set to 0 to use a device instead
-USE_EMULATOR=0
-
-# ------------------------
-
-if [ "$USE_EMULATOR" == 1 ]; then
-  emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT &
-  pid_emulator=$!
-  ADB="adb -s emulator-$EMULATOR_PORT "
-else
-  ADB="adb -d "
-fi
-
-echo "waiting to connect to device"
-$ADB wait-for-device
-
-# run a command on the device; exit with the exit status of the device
-# command.
-run_command() {
-  $ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}'
-}
-
-testname() {
-  echo
-  echo "::: testing $1 :::"
-  testname="$1"
-}
-
-fail() {
-  echo
-  echo FAIL: $testname
-  echo
-  [ "$open_pid" == "" ] || kill $open_pid
-  [ "$pid_emulator" == "" ] || kill $pid_emulator
-  exit 1
-}
-
-
-cleanup() {
-  # not necessary if we're about to kill the emulator, but nice for
-  # running on real devices or already-running emulators.
-  run_command rm $WORK_DIR/verifier_test
-  run_command rm $WORK_DIR/package.zip
-
-  [ "$pid_emulator" == "" ] || kill $pid_emulator
-}
-
-$ADB push $ANDROID_PRODUCT_OUT/system/bin/verifier_test \
-          $WORK_DIR/verifier_test
-
-expect_succeed() {
-  testname "$1 (should succeed)"
-  $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip
-  shift
-  run_command $WORK_DIR/verifier_test "$@" $WORK_DIR/package.zip || fail
-}
-
-expect_fail() {
-  testname "$1 (should fail)"
-  $ADB push $DATA_DIR/$1 $WORK_DIR/package.zip
-  shift
-  run_command $WORK_DIR/verifier_test "$@" $WORK_DIR/package.zip && fail
-}
-
-# not signed at all
-expect_fail unsigned.zip
-# signed in the pre-donut way
-expect_fail jarsigned.zip
-
-# success cases
-expect_succeed otasigned.zip -e3
-expect_succeed otasigned_f4.zip -f4
-expect_succeed otasigned_sha256.zip -e3 -sha256
-expect_succeed otasigned_f4_sha256.zip -f4 -sha256
-expect_succeed otasigned_ecdsa_sha256.zip -ec -sha256
-
-# success with multiple keys
-expect_succeed otasigned.zip -f4 -e3
-expect_succeed otasigned_f4.zip -ec -f4
-expect_succeed otasigned_sha256.zip -ec -e3 -e3 -sha256
-expect_succeed otasigned_f4_sha256.zip -ec -sha256 -e3 -f4 -sha256
-expect_succeed otasigned_ecdsa_sha256.zip -f4 -sha256 -e3 -ec -sha256
-
-# verified against different key
-expect_fail otasigned.zip -f4
-expect_fail otasigned_f4.zip -e3
-expect_fail otasigned_ecdsa_sha256.zip -e3 -sha256
-
-# verified against right key but wrong hash algorithm
-expect_fail otasigned.zip -e3 -sha256
-expect_fail otasigned_f4.zip -f4 -sha256
-expect_fail otasigned_sha256.zip
-expect_fail otasigned_f4_sha256.zip -f4
-expect_fail otasigned_ecdsa_sha256.zip
-
-# various other cases
-expect_fail random.zip
-expect_fail fake-eocd.zip
-expect_fail alter-metadata.zip
-expect_fail alter-footer.zip
-
-# --------------- cleanup ----------------------
-
-cleanup
-
-echo
-echo PASS
-echo