merge in oc-release history after reset to master
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index 9d505f9..9b84fa1 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "applypatch/applypatch.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <libgen.h>
@@ -27,16 +29,18 @@
 
 #include <memory>
 #include <string>
+#include <utility>
+#include <vector>
 
+#include <android-base/parseint.h>
 #include <android-base/strings.h>
+#include <openssl/sha.h>
 
-#include "openssl/sha.h"
-#include "applypatch/applypatch.h"
 #include "edify/expr.h"
 #include "ota_io.h"
 #include "print_sha1.h"
 
-static int LoadPartitionContents(const char* filename, FileContents* file);
+static int LoadPartitionContents(const std::string& filename, FileContents* file);
 static ssize_t FileSink(const unsigned char* data, ssize_t len, void* token);
 static int GenerateTarget(FileContents* source_file,
                           const Value* source_patch_value,
@@ -48,39 +52,34 @@
                           size_t target_size,
                           const Value* bonus_data);
 
-// Read a file into memory; store the file contents and associated
-// metadata in *file.
-//
+// Read a file into memory; store the file contents and associated metadata in *file.
 // Return 0 on success.
 int LoadFileContents(const char* filename, FileContents* file) {
-    // A special 'filename' beginning with "EMMC:" means to
-    // load the contents of a partition.
-    if (strncmp(filename, "EMMC:", 5) == 0) {
-        return LoadPartitionContents(filename, file);
-    }
+  // A special 'filename' beginning with "EMMC:" means to load the contents of a partition.
+  if (strncmp(filename, "EMMC:", 5) == 0) {
+    return LoadPartitionContents(filename, file);
+  }
 
-    if (stat(filename, &file->st) != 0) {
-        printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
-        return -1;
-    }
+  if (stat(filename, &file->st) == -1) {
+    printf("failed to stat \"%s\": %s\n", filename, strerror(errno));
+    return -1;
+  }
 
-    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));
-        return -1;
-    }
+  std::vector<unsigned char> data(file->st.st_size);
+  std::unique_ptr<FILE, decltype(&ota_fclose)> f(ota_fopen(filename, "rb"), ota_fclose);
+  if (!f) {
+    printf("failed to open \"%s\": %s\n", filename, strerror(errno));
+    return -1;
+  }
 
-    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;
-    }
-    ota_fclose(f);
-    file->data = std::move(data);
-    SHA1(file->data.data(), file->data.size(), file->sha1);
-    return 0;
+  size_t bytes_read = ota_fread(data.data(), 1, data.size(), f.get());
+  if (bytes_read != data.size()) {
+    printf("short read of \"%s\" (%zu bytes of %zu)\n", filename, bytes_read, data.size());
+    return -1;
+  }
+  file->data = std::move(data);
+  SHA1(file->data.data(), file->data.size(), file->sha1);
+  return 0;
 }
 
 // Load the contents of an EMMC partition into the provided
@@ -97,285 +96,258 @@
 // "end-of-file" marker), so the caller must specify the possible
 // lengths and the hash of the data, and we'll do the load expecting
 // to find one of those hashes.
-static int LoadPartitionContents(const char* filename, FileContents* file) {
-    std::string copy(filename);
-    std::vector<std::string> pieces = android::base::Split(copy, ":");
-    if (pieces.size() < 4 || pieces.size() % 2 != 0) {
-        printf("LoadPartitionContents called with bad filename (%s)\n", filename);
+static int LoadPartitionContents(const std::string& filename, FileContents* file) {
+  std::vector<std::string> pieces = android::base::Split(filename, ":");
+  if (pieces.size() < 4 || pieces.size() % 2 != 0 || pieces[0] != "EMMC") {
+    printf("LoadPartitionContents called with bad filename \"%s\"\n", filename.c_str());
+    return -1;
+  }
+
+  size_t pair_count = (pieces.size() - 2) / 2;  // # of (size, sha1) pairs in filename
+  std::vector<std::pair<size_t, std::string>> pairs;
+  for (size_t i = 0; i < pair_count; ++i) {
+    size_t size;
+    if (!android::base::ParseUint(pieces[i * 2 + 2], &size) || size == 0) {
+      printf("LoadPartitionContents called with bad size \"%s\"\n", pieces[i * 2 + 2].c_str());
+      return -1;
+    }
+    pairs.push_back({ size, pieces[i * 2 + 3] });
+  }
+
+  // Sort the pairs array so that they are in order of increasing size.
+  std::sort(pairs.begin(), pairs.end());
+
+  const char* partition = pieces[1].c_str();
+  std::unique_ptr<FILE, decltype(&ota_fclose)> dev(ota_fopen(partition, "rb"), ota_fclose);
+  if (!dev) {
+    printf("failed to open emmc partition \"%s\": %s\n", partition, strerror(errno));
+    return -1;
+  }
+
+  SHA_CTX sha_ctx;
+  SHA1_Init(&sha_ctx);
+
+  // Allocate enough memory to hold the largest size.
+  std::vector<unsigned char> buffer(pairs[pair_count - 1].first);
+  unsigned char* buffer_ptr = buffer.data();
+  size_t buffer_size = 0;  // # bytes read so far
+  bool found = false;
+
+  for (const auto& pair : pairs) {
+    size_t current_size = pair.first;
+    const std::string& current_sha1 = pair.second;
+
+    // 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 = current_size - buffer_size;
+    if (next > 0) {
+      size_t read = ota_fread(buffer_ptr, 1, next, dev.get());
+      if (next != read) {
+        printf("short read (%zu bytes of %zu) for partition \"%s\"\n", read, next, partition);
         return -1;
+      }
+      SHA1_Update(&sha_ctx, buffer_ptr, read);
+      buffer_size += read;
+      buffer_ptr += read;
     }
 
-    if (pieces[0] != "EMMC") {
-        printf("LoadPartitionContents called with bad filename (%s)\n", filename);
-        return -1;
-    }
-    const char* partition = pieces[1].c_str();
+    // 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));
+    uint8_t sha_so_far[SHA_DIGEST_LENGTH];
+    SHA1_Final(sha_so_far, &temp_ctx);
 
-    size_t pairs = (pieces.size() - 2) / 2;    // # of (size, sha1) pairs in filename
-    std::vector<size_t> index(pairs);
-    std::vector<size_t> size(pairs);
-    std::vector<std::string> sha1sum(pairs);
-
-    for (size_t i = 0; i < pairs; ++i) {
-        size[i] = strtol(pieces[i*2+2].c_str(), NULL, 10);
-        if (size[i] == 0) {
-            printf("LoadPartitionContents called with bad size (%s)\n", filename);
-            return -1;
-        }
-        sha1sum[i] = pieces[i*2+3].c_str();
-        index[i] = i;
-    }
-
-    // Sort the index[] array so it indexes the pairs in order of increasing size.
-    sort(index.begin(), index.end(),
-        [&](const size_t& i, const size_t& j) {
-            return (size[i] < size[j]);
-        }
-    );
-
-    FILE* dev = ota_fopen(partition, "rb");
-    if (dev == NULL) {
-        printf("failed to open emmc partition \"%s\": %s\n", partition, strerror(errno));
-        return -1;
-    }
-
-    SHA_CTX sha_ctx;
-    SHA1_Init(&sha_ctx);
     uint8_t parsed_sha[SHA_DIGEST_LENGTH];
-
-    // Allocate enough memory to hold the largest size.
-    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]] - data_size;
-        if (next > 0) {
-            size_t read = ota_fread(p, 1, next, dev);
-            if (next != read) {
-                printf("short read (%zu bytes of %zu) for partition \"%s\"\n",
-                       read, next, partition);
-                return -1;
-            }
-            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));
-        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);
-            return -1;
-        }
-
-        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",
-                   size[index[i]], sha1sum[index[i]].c_str());
-            found = true;
-            break;
-        }
+    if (ParseSha1(current_sha1.c_str(), parsed_sha) != 0) {
+      printf("failed to parse SHA-1 %s in %s\n", current_sha1.c_str(), filename.c_str());
+      return -1;
     }
 
-    ota_fclose(dev);
-
-    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);
-        return -1;
+    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-1 %s\n", current_size, current_sha1.c_str());
+      found = true;
+      break;
     }
+  }
 
-    SHA1_Final(file->sha1, &sha_ctx);
+  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.c_str());
+    return -1;
+  }
 
-    data.resize(data_size);
-    file->data = std::move(data);
-    // Fake some stat() info.
-    file->st.st_mode = 0644;
-    file->st.st_uid = 0;
-    file->st.st_gid = 0;
+  SHA1_Final(file->sha1, &sha_ctx);
 
-    return 0;
+  buffer.resize(buffer_size);
+  file->data = std::move(buffer);
+  // Fake some stat() info.
+  file->st.st_mode = 0644;
+  file->st.st_uid = 0;
+  file->st.st_gid = 0;
+
+  return 0;
 }
 
 
 // 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 = 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;
-    }
+  unique_fd fd(ota_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR));
+  if (fd == -1) {
+    printf("failed to open \"%s\" for write: %s\n", filename, strerror(errno));
+    return -1;
+  }
 
-    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 (ota_fsync(fd) != 0) {
-        printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno));
-        return -1;
-    }
-    if (ota_close(fd) != 0) {
-        printf("close of \"%s\" failed: %s\n", filename, strerror(errno));
-        return -1;
-    }
+  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));
+    return -1;
+  }
+  if (ota_fsync(fd) != 0) {
+    printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno));
+    return -1;
+  }
+  if (ota_close(fd) != 0) {
+    printf("close of \"%s\" failed: %s\n", filename, strerror(errno));
+    return -1;
+  }
 
-    if (chmod(filename, file->st.st_mode) != 0) {
-        printf("chmod of \"%s\" failed: %s\n", filename, strerror(errno));
-        return -1;
-    }
-    if (chown(filename, file->st.st_uid, file->st.st_gid) != 0) {
-        printf("chown of \"%s\" failed: %s\n", filename, strerror(errno));
-        return -1;
-    }
+  if (chmod(filename, file->st.st_mode) != 0) {
+    printf("chmod of \"%s\" failed: %s\n", filename, strerror(errno));
+    return -1;
+  }
+  if (chown(filename, file->st.st_uid, file->st.st_gid) != 0) {
+    printf("chown of \"%s\" failed: %s\n", filename, strerror(errno));
+    return -1;
+  }
 
-    return 0;
+  return 0;
 }
 
 // Write a memory buffer to 'target' partition, a string of the form
 // "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(const unsigned char* data, size_t len, const char* target) {
-    std::string copy(target);
-    std::vector<std::string> pieces = android::base::Split(copy, ":");
+int WriteToPartition(const unsigned char* data, size_t len, const std::string& target) {
+  std::vector<std::string> pieces = android::base::Split(target, ":");
+  if (pieces.size() < 2 || pieces[0] != "EMMC") {
+    printf("WriteToPartition called with bad target (%s)\n", target.c_str());
+    return -1;
+  }
 
-    if (pieces.size() < 2) {
-        printf("WriteToPartition called with bad target (%s)\n", target);
+  const char* partition = pieces[1].c_str();
+  unique_fd fd(ota_open(partition, O_RDWR));
+  if (fd == -1) {
+    printf("failed to open %s: %s\n", partition, strerror(errno));
+    return -1;
+  }
+
+  size_t start = 0;
+  bool success = false;
+  for (size_t attempt = 0; attempt < 2; ++attempt) {
+    if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) {
+      printf("failed seek on %s: %s\n", partition, strerror(errno));
+      return -1;
+    }
+    while (start < len) {
+      size_t to_write = len - start;
+      if (to_write > 1 << 20) to_write = 1 << 20;
+
+      ssize_t written = TEMP_FAILURE_RETRY(ota_write(fd, data + start, to_write));
+      if (written == -1) {
+        printf("failed write writing to %s: %s\n", partition, strerror(errno));
         return -1;
+      }
+      start += written;
     }
 
-    if (pieces[0] != "EMMC") {
-        printf("WriteToPartition called with bad target (%s)\n", target);
-        return -1;
+    if (ota_fsync(fd) != 0) {
+      printf("failed to sync to %s: %s\n", partition, strerror(errno));
+      return -1;
     }
-    const char* partition = pieces[1].c_str();
-
-    size_t start = 0;
-    bool success = false;
-    int fd = ota_open(partition, O_RDWR | O_SYNC);
-    if (fd < 0) {
-        printf("failed to open %s: %s\n", partition, strerror(errno));
-        return -1;
-    }
-
-    for (size_t attempt = 0; attempt < 2; ++attempt) {
-        if (TEMP_FAILURE_RETRY(lseek(fd, start, SEEK_SET)) == -1) {
-            printf("failed seek on %s: %s\n", partition, strerror(errno));
-            return -1;
-        }
-        while (start < len) {
-            size_t to_write = len - start;
-            if (to_write > 1<<20) to_write = 1<<20;
-
-            ssize_t written = TEMP_FAILURE_RETRY(ota_write(fd, data+start, to_write));
-            if (written == -1) {
-                printf("failed write writing to %s: %s\n", partition, strerror(errno));
-                return -1;
-            }
-            start += written;
-        }
-        if (ota_fsync(fd) != 0) {
-            printf("failed to sync to %s (%s)\n", partition, strerror(errno));
-            return -1;
-        }
-        if (ota_close(fd) != 0) {
-            printf("failed to close %s (%s)\n", partition, strerror(errno));
-            return -1;
-        }
-        fd = ota_open(partition, O_RDONLY);
-        if (fd < 0) {
-            printf("failed to reopen %s for verify (%s)\n", partition, strerror(errno));
-            return -1;
-        }
-
-        // Drop caches so our subsequent verification read
-        // won't just be reading the cache.
-        sync();
-        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");
-        }
-        ota_close(dc);
-        sleep(1);
-
-        // verify
-        if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) {
-            printf("failed to seek back to beginning of %s: %s\n",
-                   partition, strerror(errno));
-            return -1;
-        }
-        unsigned char buffer[4096];
-        start = len;
-        for (size_t p = 0; p < len; p += sizeof(buffer)) {
-            size_t to_read = len - p;
-            if (to_read > sizeof(buffer)) {
-                to_read = sizeof(buffer);
-            }
-
-            size_t so_far = 0;
-            while (so_far < to_read) {
-                ssize_t read_count =
-                    TEMP_FAILURE_RETRY(ota_read(fd, buffer+so_far, to_read-so_far));
-                if (read_count == -1) {
-                    printf("verify read error %s at %zu: %s\n",
-                           partition, p, strerror(errno));
-                    return -1;
-                } else if (read_count == 0) {
-                    printf("verify read reached unexpected EOF, %s at %zu\n", partition, p);
-                    return -1;
-                }
-                if (static_cast<size_t>(read_count) < to_read) {
-                    printf("short verify read %s at %zu: %zd %zu %s\n",
-                           partition, p, read_count, to_read, strerror(errno));
-                }
-                so_far += read_count;
-            }
-
-            if (memcmp(buffer, data+p, to_read) != 0) {
-                printf("verification failed starting at %zu\n", p);
-                start = p;
-                break;
-            }
-        }
-
-        if (start == len) {
-            printf("verification read succeeded (attempt %zu)\n", attempt+1);
-            success = true;
-            break;
-        }
-    }
-
-    if (!success) {
-        printf("failed to verify after all attempts\n");
-        return -1;
-    }
-
     if (ota_close(fd) != 0) {
-        printf("error closing %s (%s)\n", partition, strerror(errno));
-        return -1;
+      printf("failed to close %s: %s\n", partition, strerror(errno));
+      return -1;
     }
+
+    fd.reset(ota_open(partition, O_RDONLY));
+    if (fd == -1) {
+      printf("failed to reopen %s for verify: %s\n", partition, strerror(errno));
+      return -1;
+    }
+
+    // Drop caches so our subsequent verification read won't just be reading the cache.
     sync();
+    unique_fd dc(ota_open("/proc/sys/vm/drop_caches", O_WRONLY));
+    if (TEMP_FAILURE_RETRY(ota_write(dc, "3\n", 2)) == -1) {
+      printf("write to /proc/sys/vm/drop_caches failed: %s\n", strerror(errno));
+    } else {
+      printf("  caches dropped\n");
+    }
+    ota_close(dc);
+    sleep(1);
 
-    return 0;
+    // Verify.
+    if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) {
+      printf("failed to seek back to beginning of %s: %s\n", partition, strerror(errno));
+      return -1;
+    }
+
+    unsigned char buffer[4096];
+    start = len;
+    for (size_t p = 0; p < len; p += sizeof(buffer)) {
+      size_t to_read = len - p;
+      if (to_read > sizeof(buffer)) {
+        to_read = sizeof(buffer);
+      }
+
+      size_t so_far = 0;
+      while (so_far < to_read) {
+        ssize_t read_count = TEMP_FAILURE_RETRY(ota_read(fd, buffer + so_far, to_read - so_far));
+        if (read_count == -1) {
+          printf("verify read error %s at %zu: %s\n", partition, p, strerror(errno));
+          return -1;
+        } else if (read_count == 0) {
+          printf("verify read reached unexpected EOF, %s at %zu\n", partition, p);
+          return -1;
+        }
+        if (static_cast<size_t>(read_count) < to_read) {
+          printf("short verify read %s at %zu: %zd %zu\n", partition, p, read_count, to_read);
+        }
+        so_far += read_count;
+      }
+
+      if (memcmp(buffer, data + p, to_read) != 0) {
+        printf("verification failed starting at %zu\n", p);
+        start = p;
+        break;
+      }
+    }
+
+    if (start == len) {
+      printf("verification read succeeded (attempt %zu)\n", attempt + 1);
+      success = true;
+      break;
+    }
+  }
+
+  if (!success) {
+    printf("failed to verify after all attempts\n");
+    return -1;
+  }
+
+  if (ota_close(fd) == -1) {
+    printf("error closing %s: %s\n", partition, strerror(errno));
+    return -1;
+  }
+  sync();
+
+  return 0;
 }
 
-
 // Take a string 'str' of 40 hex digits and parse it into the 20
 // byte array 'digest'.  'str' may contain only the digest or be of
 // the form "<digest>:<anything>".  Return 0 on success, -1 on any
@@ -409,48 +381,46 @@
 // Return the index of the match on success, or -1 if no match is
 // found.
 int FindMatchingPatch(uint8_t* sha1, const std::vector<std::string>& patch_sha1_str) {
-    for (size_t i = 0; i < patch_sha1_str.size(); ++i) {
-        uint8_t patch_sha1[SHA_DIGEST_LENGTH];
-        if (ParseSha1(patch_sha1_str[i].c_str(), patch_sha1) == 0 &&
-            memcmp(patch_sha1, sha1, SHA_DIGEST_LENGTH) == 0) {
-            return i;
-        }
+  for (size_t i = 0; i < patch_sha1_str.size(); ++i) {
+    uint8_t patch_sha1[SHA_DIGEST_LENGTH];
+    if (ParseSha1(patch_sha1_str[i].c_str(), patch_sha1) == 0 &&
+        memcmp(patch_sha1, sha1, SHA_DIGEST_LENGTH) == 0) {
+      return i;
     }
-    return -1;
+  }
+  return -1;
 }
 
 // Returns 0 if the contents of the file (argv[2]) or the cached file
 // match any of the sha1's on the command line (argv[3:]).  Returns
 // nonzero otherwise.
 int applypatch_check(const char* filename, const std::vector<std::string>& patch_sha1_str) {
-    FileContents file;
+  FileContents file;
 
-    // It's okay to specify no sha1s; the check will pass if the
-    // LoadFileContents is successful.  (Useful for reading
-    // partitions, where the filename encodes the sha1s; no need to
-    // check them twice.)
-    if (LoadFileContents(filename, &file) != 0 ||
-        (patch_sha1_str.size() > 0 && FindMatchingPatch(file.sha1, patch_sha1_str) < 0)) {
-        printf("file \"%s\" doesn't have any of expected "
-               "sha1 sums; checking cache\n", filename);
+  // It's okay to specify no sha1s; the check will pass if the
+  // LoadFileContents is successful.  (Useful for reading
+  // partitions, where the filename encodes the sha1s; no need to
+  // check them twice.)
+  if (LoadFileContents(filename, &file) != 0 ||
+      (!patch_sha1_str.empty() && FindMatchingPatch(file.sha1, patch_sha1_str) < 0)) {
+    printf("file \"%s\" doesn't have any of expected sha1 sums; checking cache\n", filename);
 
-        // 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
-        // exists and matches the sha1 we're looking for, the check still
-        // passes.
-
-        if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
-            printf("failed to load cache file\n");
-            return 1;
-        }
-
-        if (FindMatchingPatch(file.sha1, patch_sha1_str) < 0) {
-            printf("cache bits don't match any sha1 for \"%s\"\n", filename);
-            return 1;
-        }
+    // 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
+    // exists and matches the sha1 we're looking for, the check still
+    // passes.
+    if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
+      printf("failed to load cache file\n");
+      return 1;
     }
-    return 0;
+
+    if (FindMatchingPatch(file.sha1, patch_sha1_str) < 0) {
+      printf("cache bits don't match any sha1 for \"%s\"\n", filename);
+      return 1;
+    }
+  }
+  return 0;
 }
 
 int ShowLicenses() {
@@ -544,10 +514,8 @@
         return 1;
     }
 
-    FileContents copy_file;
     FileContents source_file;
     const Value* source_patch_value = nullptr;
-    const Value* copy_patch_value = nullptr;
 
     // We try to load the target file into the source_file object.
     if (LoadFileContents(target_filename, &source_file) == 0) {
@@ -575,6 +543,8 @@
         }
     }
 
+    FileContents copy_file;
+    const Value* copy_patch_value = nullptr;
     if (source_patch_value == nullptr) {
         source_file.data.clear();
         printf("source file is bad; trying copy\n");
@@ -612,50 +582,49 @@
  */
 int applypatch_flash(const char* source_filename, const char* target_filename,
                      const char* target_sha1_str, size_t target_size) {
-    printf("flash %s: ", target_filename);
+  printf("flash %s: ", target_filename);
 
-    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;
-    }
+  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;
-    std::string target_str(target_filename);
+  std::string target_str(target_filename);
+  std::vector<std::string> pieces = android::base::Split(target_str, ":");
+  if (pieces.size() != 2 || pieces[0] != "EMMC") {
+    printf("invalid target name \"%s\"", target_filename);
+    return 1;
+  }
 
-    std::vector<std::string> pieces = android::base::Split(target_str, ":");
-    if (pieces.size() != 2 || pieces[0] != "EMMC") {
-        printf("invalid target name \"%s\"", target_filename);
-        return 1;
-    }
-
-    // Load the target into the source_file object to see if already applied.
-    pieces.push_back(std::to_string(target_size));
-    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_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());
-        return 0;
-    }
-
-    if (LoadFileContents(source_filename, &source_file) == 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());
-            return 1;
-        }
-    }
-
-    if (WriteToPartition(source_file.data.data(), target_size, target_filename) != 0) {
-        printf("write of copied data to %s failed\n", target_filename);
-        return 1;
-    }
+  // Load the target into the source_file object to see if already applied.
+  pieces.push_back(std::to_string(target_size));
+  pieces.push_back(target_sha1_str);
+  std::string fullname = android::base::Join(pieces, ':');
+  FileContents source_file;
+  if (LoadPartitionContents(fullname, &source_file) == 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());
     return 0;
+  }
+
+  if (LoadFileContents(source_filename, &source_file) == 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());
+      return 1;
+    }
+  }
+
+  if (WriteToPartition(source_file.data.data(), target_size, target_filename) != 0) {
+    printf("write of copied data to %s failed\n", target_filename);
+    return 1;
+  }
+  return 0;
 }
 
 static int GenerateTarget(FileContents* source_file,
@@ -667,221 +636,214 @@
                           const uint8_t target_sha1[SHA_DIGEST_LENGTH],
                           size_t target_size,
                           const Value* bonus_data) {
-    int retry = 1;
-    SHA_CTX ctx;
-    std::string memory_sink_str;
-    FileContents* source_to_use;
-    int made_copy = 0;
+  // 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().
+  std::string target_fs = target_filename;
+  auto slash_pos = target_fs.find('/', 1);
+  if (slash_pos != std::string::npos) {
+    target_fs.resize(slash_pos);
+  }
 
-    bool target_is_partition = (strncmp(target_filename, "EMMC:", 5) == 0);
-    const std::string tmp_target_filename = std::string(target_filename) + ".patch";
+  FileContents* source_to_use;
+  const Value* patch;
+  if (source_patch_value != nullptr) {
+    source_to_use = source_file;
+    patch = source_patch_value;
+  } else {
+    source_to_use = copy_file;
+    patch = copy_patch_value;
+  }
 
-    // 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().
-    std::string target_fs = target_filename;
-    auto slash_pos = target_fs.find('/', 1);
-    if (slash_pos != std::string::npos) {
-        target_fs.resize(slash_pos);
-    }
+  if (patch->type != VAL_BLOB) {
+    printf("patch is not a blob\n");
+    return 1;
+  }
 
-    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;
-    }
-    const char* header = &patch->data[0];
-    size_t header_bytes_read = patch->data.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;
-    }
+  const char* header = &patch->data[0];
+  size_t header_bytes_read = patch->data.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?
+  bool target_is_partition = (strncmp(target_filename, "EMMC:", 5) == 0);
+  const std::string tmp_target_filename = std::string(target_filename) + ".patch";
 
-        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
-            // /tmp, so instead we'll just assume that /tmp has enough
-            // space to hold the file.
-
-            // We still write the original source to cache, in case
-            // the partition write is interrupted.
-            if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
-                printf("not enough free space on /cache\n");
-                return 1;
-            }
-            if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
-                printf("failed to back up source file\n");
-                return 1;
-            }
-            made_copy = 1;
-            retry = 0;
-        } else {
-            int enough_space = 0;
-            if (retry > 0) {
-                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
-                if (!enough_space) {
-                    printf("target %zu bytes; free space %zu bytes; retry %d; enough %d\n",
-                           target_size, free_space, retry, enough_space);
-                }
-            }
-
-            if (!enough_space) {
-                retry = 0;
-            }
-
-            if (!enough_space && source_patch_value != NULL) {
-                // Using the original source, but not enough free space.  First
-                // copy the source file to cache, then delete it from the original
-                // location.
-
-                if (strncmp(source_filename, "EMMC:", 5) == 0) {
-                    // It's impossible to free space on the target filesystem by
-                    // deleting the source if the source is a partition.  If
-                    // we're ever in a state where we need to do this, fail.
-                    printf("not enough free space for target but source is partition\n");
-                    return 1;
-                }
-
-                if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
-                    printf("not enough free space on /cache\n");
-                    return 1;
-                }
-
-                if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
-                    printf("failed to back up source file\n");
-                    return 1;
-                }
-                made_copy = 1;
-                unlink(source_filename);
-
-                size_t free_space = FreeSpaceForFile(target_fs.c_str());
-                printf("(now %zu bytes free for target) ", free_space);
-            }
-        }
-
-
-        SinkFn sink = NULL;
-        void* token = NULL;
-        int output_fd = -1;
-        if (target_is_partition) {
-            // We store the decoded output in memory.
-            sink = MemorySink;
-            token = &memory_sink_str;
-        } else {
-            // We write the decoded output to "<tgt-file>.patch".
-            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_fd;
-        }
-
-
-        SHA1_Init(&ctx);
-
-        int result;
-        if (use_bsdiff) {
-            result = ApplyBSDiffPatch(source_to_use->data.data(), source_to_use->data.size(),
-                                      patch, 0, sink, token, &ctx);
-        } else {
-            result = ApplyImagePatch(source_to_use->data.data(), source_to_use->data.size(),
-                                     patch, sink, token, &ctx, bonus_data);
-        }
-
-        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 (ota_close(output_fd) != 0) {
-                printf("failed to close file \"%s\" (%s)\n", tmp_target_filename.c_str(),
-                       strerror(errno));
-                result = 1;
-            }
-        }
-
-        if (result != 0) {
-            if (retry == 0) {
-                printf("applying patch failed\n");
-                return result != 0;
-            } else {
-                printf("applying patch failed; retrying\n");
-            }
-            if (!target_is_partition) {
-                unlink(tmp_target_filename.c_str());
-            }
-        } else {
-            // succeeded; no need to retry
-            break;
-        }
-    } while (retry-- > 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());
-    }
+  int retry = 1;
+  bool made_copy = false;
+  SHA_CTX ctx;
+  std::string memory_sink_str;  // Don't need to reserve space.
+  do {
+    // Is there enough room in the target filesystem to hold the patched file?
 
     if (target_is_partition) {
-        // Copy the temp file to the partition.
-        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;
-        }
+      // 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
+      // /tmp, so instead we'll just assume that /tmp has enough
+      // space to hold the file.
+
+      // We still write the original source to cache, in case
+      // the partition write is interrupted.
+      if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
+        printf("not enough free space on /cache\n");
+        return 1;
+      }
+      if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
+        printf("failed to back up source file\n");
+        return 1;
+      }
+      made_copy = true;
+      retry = 0;
     } else {
-        // Give the .patch file the same owner, group, and mode of the
-        // original source file.
-        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;
+      bool enough_space = false;
+      if (retry > 0) {
+        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
+        if (!enough_space) {
+          printf("target %zu bytes; free space %zu bytes; retry %d; enough %d\n", target_size,
+                 free_space, retry, enough_space);
         }
-        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;
+      }
+
+      if (!enough_space) {
+        retry = 0;
+      }
+
+      if (!enough_space && source_patch_value != nullptr) {
+        // Using the original source, but not enough free space.  First
+        // copy the source file to cache, then delete it from the original
+        // location.
+
+        if (strncmp(source_filename, "EMMC:", 5) == 0) {
+          // It's impossible to free space on the target filesystem by
+          // deleting the source if the source is a partition.  If
+          // we're ever in a state where we need to do this, fail.
+          printf("not enough free space for target but source is partition\n");
+          return 1;
         }
 
-        // Finally, rename the .patch file to replace the target file.
-        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;
+        if (MakeFreeSpaceOnCache(source_file->data.size()) < 0) {
+          printf("not enough free space on /cache\n");
+          return 1;
         }
+
+        if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
+          printf("failed to back up source file\n");
+          return 1;
+        }
+        made_copy = true;
+        unlink(source_filename);
+
+        size_t free_space = FreeSpaceForFile(target_fs.c_str());
+        printf("(now %zu bytes free for target) ", free_space);
+      }
     }
 
-    // If this run of applypatch created the copy, and we're here, we
-    // can delete it.
-    if (made_copy) {
-        unlink(CACHE_TEMP_SOURCE);
+    SinkFn sink = nullptr;
+    void* token = nullptr;
+    unique_fd output_fd;
+    if (target_is_partition) {
+      // We store the decoded output in memory.
+      sink = MemorySink;
+      token = &memory_sink_str;
+    } else {
+      // We write the decoded output to "<tgt-file>.patch".
+      output_fd.reset(ota_open(tmp_target_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_SYNC,
+                               S_IRUSR | S_IWUSR));
+      if (output_fd == -1) {
+        printf("failed to open output file %s: %s\n", tmp_target_filename.c_str(), strerror(errno));
+        return 1;
+      }
+      sink = FileSink;
+      token = &output_fd;
     }
 
-    // Success!
-    return 0;
+    SHA1_Init(&ctx);
+
+    int result;
+    if (use_bsdiff) {
+      result = ApplyBSDiffPatch(source_to_use->data.data(), source_to_use->data.size(), patch, 0,
+                                sink, token, &ctx);
+    } else {
+      result = ApplyImagePatch(source_to_use->data.data(), source_to_use->data.size(), patch, sink,
+                               token, &ctx, bonus_data);
+    }
+
+    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 (ota_close(output_fd) != 0) {
+        printf("failed to close file \"%s\": %s\n", tmp_target_filename.c_str(), strerror(errno));
+        result = 1;
+      }
+    }
+
+    if (result != 0) {
+      if (retry == 0) {
+        printf("applying patch failed\n");
+        return 1;
+      } else {
+        printf("applying patch failed; retrying\n");
+      }
+      if (!target_is_partition) {
+        unlink(tmp_target_filename.c_str());
+      }
+    } else {
+      // succeeded; no need to retry
+      break;
+    }
+  } while (retry-- > 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 (target_is_partition) {
+    // Copy the temp file to the partition.
+    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;
+    }
+  } else {
+    // Give the .patch file the same owner, group, and mode of the original source file.
+    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(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(tmp_target_filename.c_str(), target_filename) != 0) {
+      printf("rename of .patch to \"%s\" failed: %s\n", target_filename, strerror(errno));
+      return 1;
+    }
+  }
+
+  // If this run of applypatch created the copy, and we're here, we can delete it.
+  if (made_copy) {
+    unlink(CACHE_TEMP_SOURCE);
+  }
+
+  // Success!
+  return 0;
 }
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
index 16e8df1..9a56718 100644
--- a/bootloader_message/bootloader_message.cpp
+++ b/bootloader_message/bootloader_message.cpp
@@ -80,26 +80,23 @@
   return ret == 0;
 }
 
-static bool read_misc_partition(void* p, size_t size, size_t offset, std::string* err) {
-  std::string misc_blk_device = get_misc_blk_device(err);
-  if (misc_blk_device.empty()) {
-    return false;
-  }
+static bool read_misc_partition(void* p, size_t size, const std::string& misc_blk_device,
+                                size_t offset, std::string* err) {
   if (!wait_for_device(misc_blk_device, err)) {
     return false;
   }
   android::base::unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY));
-  if (fd.get() == -1) {
+  if (fd == -1) {
     *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
                                        strerror(errno));
     return false;
   }
-  if (lseek(fd.get(), static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
+  if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
     *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
                                        strerror(errno));
     return false;
   }
-  if (!android::base::ReadFully(fd.get(), p, size)) {
+  if (!android::base::ReadFully(fd, p, size)) {
     *err = android::base::StringPrintf("failed to read %s: %s", misc_blk_device.c_str(),
                                        strerror(errno));
     return false;
@@ -107,30 +104,25 @@
   return true;
 }
 
-static bool write_misc_partition(const void* p, size_t size, size_t offset, std::string* err) {
-  std::string misc_blk_device = get_misc_blk_device(err);
-  if (misc_blk_device.empty()) {
-    return false;
-  }
-  android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY | O_SYNC));
-  if (fd.get() == -1) {
+static bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
+                                 size_t offset, std::string* err) {
+  android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY));
+  if (fd == -1) {
     *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
                                        strerror(errno));
     return false;
   }
-  if (lseek(fd.get(), static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
+  if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
     *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
                                        strerror(errno));
     return false;
   }
-  if (!android::base::WriteFully(fd.get(), p, size)) {
+  if (!android::base::WriteFully(fd, p, size)) {
     *err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(),
                                        strerror(errno));
     return false;
   }
-
-  // TODO: O_SYNC and fsync duplicates each other?
-  if (fsync(fd.get()) == -1) {
+  if (fsync(fd) == -1) {
     *err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(),
                                        strerror(errno));
     return false;
@@ -138,12 +130,32 @@
   return true;
 }
 
+bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
+                                  std::string* err) {
+  return read_misc_partition(boot, sizeof(*boot), misc_blk_device,
+                             BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
+}
+
 bool read_bootloader_message(bootloader_message* boot, std::string* err) {
-  return read_misc_partition(boot, sizeof(*boot), BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
+  std::string misc_blk_device = get_misc_blk_device(err);
+  if (misc_blk_device.empty()) {
+    return false;
+  }
+  return read_bootloader_message_from(boot, misc_blk_device, err);
+}
+
+bool write_bootloader_message_to(const bootloader_message& boot, const std::string& misc_blk_device,
+                                 std::string* err) {
+  return write_misc_partition(&boot, sizeof(boot), misc_blk_device,
+                              BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
 }
 
 bool write_bootloader_message(const bootloader_message& boot, std::string* err) {
-  return write_misc_partition(&boot, sizeof(boot), BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
+  std::string misc_blk_device = get_misc_blk_device(err);
+  if (misc_blk_device.empty()) {
+    return false;
+  }
+  return write_bootloader_message_to(boot, misc_blk_device, err);
 }
 
 bool clear_bootloader_message(std::string* err) {
@@ -178,12 +190,21 @@
 }
 
 bool read_wipe_package(std::string* package_data, size_t size, std::string* err) {
+  std::string misc_blk_device = get_misc_blk_device(err);
+  if (misc_blk_device.empty()) {
+    return false;
+  }
   package_data->resize(size);
-  return read_misc_partition(&(*package_data)[0], size, WIPE_PACKAGE_OFFSET_IN_MISC, err);
+  return read_misc_partition(&(*package_data)[0], size, misc_blk_device,
+                             WIPE_PACKAGE_OFFSET_IN_MISC, err);
 }
 
 bool write_wipe_package(const std::string& package_data, std::string* err) {
-  return write_misc_partition(package_data.data(), package_data.size(),
+  std::string misc_blk_device = get_misc_blk_device(err);
+  if (misc_blk_device.empty()) {
+    return false;
+  }
+  return write_misc_partition(package_data.data(), package_data.size(), misc_blk_device,
                               WIPE_PACKAGE_OFFSET_IN_MISC, err);
 }
 
diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h
index 75d2aca..b3d2182 100644
--- a/bootloader_message/include/bootloader_message/bootloader_message.h
+++ b/bootloader_message/include/bootloader_message/bootloader_message.h
@@ -179,15 +179,33 @@
 #include <string>
 #include <vector>
 
+// Read bootloader message into boot. Error message will be set in err.
 bool read_bootloader_message(bootloader_message* boot, std::string* err);
+
+// Read bootloader message from the specified misc device into boot.
+bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
+                                  std::string* err);
+
+// Write bootloader message to BCB.
 bool write_bootloader_message(const bootloader_message& boot, std::string* err);
+
+// Write bootloader message to the specified BCB device.
+bool write_bootloader_message_to(const bootloader_message& boot,
+                                 const std::string& misc_blk_device, std::string* err);
+
+// Write bootloader message (boots into recovery with the options) to BCB.
 bool write_bootloader_message(const std::vector<std::string>& options, std::string* err);
+
+// Clear BCB.
 bool clear_bootloader_message(std::string* err);
 
 // Writes the reboot-bootloader reboot reason to the bootloader_message.
 bool write_reboot_bootloader(std::string* err);
 
+// Read the wipe package from BCB (from offset WIPE_PACKAGE_OFFSET_IN_MISC).
 bool read_wipe_package(std::string* package_data, size_t size, std::string* err);
+
+// Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC).
 bool write_wipe_package(const std::string& package_data, std::string* err);
 
 #else
diff --git a/otafault/ota_io.h b/otafault/ota_io.h
index 84187a7..e119eef 100644
--- a/otafault/ota_io.h
+++ b/otafault/ota_io.h
@@ -26,6 +26,8 @@
 #include <stdio.h>
 #include <sys/stat.h>
 
+#include <android-base/unique_fd.h>
+
 #define OTAIO_CACHE_FNAME "/cache/saved.file"
 
 void ota_set_fault_files();
@@ -50,4 +52,12 @@
 
 int ota_fsync(int fd);
 
+struct OtaCloser {
+  static void Close(int fd) {
+    ota_close(fd);
+  }
+};
+
+using unique_fd = android::base::unique_fd_impl<OtaCloser>;
+
 #endif
diff --git a/tests/Android.mk b/tests/Android.mk
index fdc9470..5f6a7ce 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -80,10 +80,12 @@
     libedify \
     libotafault \
     libupdater \
+    libbootloader_message \
     libverifier \
     libminui \
     libotautil \
     libmounts \
+    libfs_mgr \
     liblog \
     libselinux \
     libext4_utils_static \
diff --git a/tests/common/test_constants.h b/tests/common/test_constants.h
index 93e4ab5..f6b6922 100644
--- a/tests/common/test_constants.h
+++ b/tests/common/test_constants.h
@@ -22,6 +22,8 @@
 // Zip entries in ziptest_valid.zip.
 static const std::string kATxtContents("abcdefghabcdefgh\n");
 static const std::string kBTxtContents("abcdefgh\n");
+static const std::string kCTxtContents("abcdefghabcdefgh\n");
+static const std::string kDTxtContents("abcdefgh\n");
 
 // echo -n -e "abcdefghabcdefgh\n" | sha1sum
 static const std::string kATxtSha1Sum("32c96a03dc8cd20097940f351bca6261ee5a1643");
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp
index a029cf4..f31f1f8 100644
--- a/tests/component/updater_test.cpp
+++ b/tests/component/updater_test.cpp
@@ -23,6 +23,7 @@
 #include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/test_utils.h>
+#include <bootloader_message/bootloader_message.h>
 #include <gtest/gtest.h>
 #include <ziparchive/zip_archive.h>
 
@@ -270,6 +271,102 @@
     ASSERT_EQ(0, unlink(src2.c_str()));
 }
 
+TEST_F(UpdaterTest, package_extract_dir) {
+  // package_extract_dir expects 2 arguments.
+  expect(nullptr, "package_extract_dir()", kArgsParsingFailure);
+  expect(nullptr, "package_extract_dir(\"arg1\")", kArgsParsingFailure);
+  expect(nullptr, "package_extract_dir(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+  std::string zip_path = from_testdata_base("ziptest_valid.zip");
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+  // Need to set up the ziphandle.
+  UpdaterInfo updater_info;
+  updater_info.package_zip = handle;
+
+  // Extract "b/c.txt" and "b/d.txt" with package_extract_dir("b", "<dir>").
+  TemporaryDir td;
+  std::string temp_dir(td.path);
+  std::string script("package_extract_dir(\"b\", \"" + temp_dir + "\")");
+  expect("t", script.c_str(), kNoCause, &updater_info);
+
+  // Verify.
+  std::string data;
+  std::string file_c = temp_dir + "/c.txt";
+  ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
+  ASSERT_EQ(kCTxtContents, data);
+
+  std::string file_d = temp_dir + "/d.txt";
+  ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
+  ASSERT_EQ(kDTxtContents, data);
+
+  // Modify the contents in order to retry. It's expected to be overwritten.
+  ASSERT_TRUE(android::base::WriteStringToFile("random", file_c));
+  ASSERT_TRUE(android::base::WriteStringToFile("random", file_d));
+
+  // Extract again and verify.
+  expect("t", script.c_str(), kNoCause, &updater_info);
+
+  ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
+  ASSERT_EQ(kCTxtContents, data);
+  ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
+  ASSERT_EQ(kDTxtContents, data);
+
+  // Clean up the temp files under td.
+  ASSERT_EQ(0, unlink(file_c.c_str()));
+  ASSERT_EQ(0, unlink(file_d.c_str()));
+
+  // Extracting "b/" (with slash) should give the same result.
+  script = "package_extract_dir(\"b/\", \"" + temp_dir + "\")";
+  expect("t", script.c_str(), kNoCause, &updater_info);
+
+  ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
+  ASSERT_EQ(kCTxtContents, data);
+  ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
+  ASSERT_EQ(kDTxtContents, data);
+
+  ASSERT_EQ(0, unlink(file_c.c_str()));
+  ASSERT_EQ(0, unlink(file_d.c_str()));
+
+  // Extracting "" is allowed. The entries will carry the path name.
+  script = "package_extract_dir(\"\", \"" + temp_dir + "\")";
+  expect("t", script.c_str(), kNoCause, &updater_info);
+
+  std::string file_a = temp_dir + "/a.txt";
+  ASSERT_TRUE(android::base::ReadFileToString(file_a, &data));
+  ASSERT_EQ(kATxtContents, data);
+  std::string file_b = temp_dir + "/b.txt";
+  ASSERT_TRUE(android::base::ReadFileToString(file_b, &data));
+  ASSERT_EQ(kBTxtContents, data);
+  std::string file_b_c = temp_dir + "/b/c.txt";
+  ASSERT_TRUE(android::base::ReadFileToString(file_b_c, &data));
+  ASSERT_EQ(kCTxtContents, data);
+  std::string file_b_d = temp_dir + "/b/d.txt";
+  ASSERT_TRUE(android::base::ReadFileToString(file_b_d, &data));
+  ASSERT_EQ(kDTxtContents, data);
+
+  ASSERT_EQ(0, unlink(file_a.c_str()));
+  ASSERT_EQ(0, unlink(file_b.c_str()));
+  ASSERT_EQ(0, unlink(file_b_c.c_str()));
+  ASSERT_EQ(0, unlink(file_b_d.c_str()));
+  ASSERT_EQ(0, rmdir((temp_dir + "/b").c_str()));
+
+  // Extracting non-existent entry should still give "t".
+  script = "package_extract_dir(\"doesntexist\", \"" + temp_dir + "\")";
+  expect("t", script.c_str(), kNoCause, &updater_info);
+
+  // Only relative zip_path is allowed.
+  script = "package_extract_dir(\"/b\", \"" + temp_dir + "\")";
+  expect("", script.c_str(), kNoCause, &updater_info);
+
+  // Only absolute dest_path is allowed.
+  script = "package_extract_dir(\"b\", \"path\")";
+  expect("", script.c_str(), kNoCause, &updater_info);
+
+  CloseArchive(handle);
+}
+
 // TODO: Test extracting to block device.
 TEST_F(UpdaterTest, package_extract_file) {
   // package_extract_file expects 1 or 2 arguments.
@@ -322,3 +419,94 @@
 
   CloseArchive(handle);
 }
+
+TEST_F(UpdaterTest, write_value) {
+  // write_value() expects two arguments.
+  expect(nullptr, "write_value()", kArgsParsingFailure);
+  expect(nullptr, "write_value(\"arg1\")", kArgsParsingFailure);
+  expect(nullptr, "write_value(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+  // filename cannot be empty.
+  expect(nullptr, "write_value(\"value\", \"\")", kArgsParsingFailure);
+
+  // Write some value to file.
+  TemporaryFile temp_file;
+  std::string value = "magicvalue";
+  std::string script("write_value(\"" + value + "\", \"" + std::string(temp_file.path) + "\")");
+  expect("t", script.c_str(), kNoCause);
+
+  // Verify the content.
+  std::string content;
+  ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &content));
+  ASSERT_EQ(value, content);
+
+  // Allow writing empty string.
+  script = "write_value(\"\", \"" + std::string(temp_file.path) + "\")";
+  expect("t", script.c_str(), kNoCause);
+
+  // Verify the content.
+  ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &content));
+  ASSERT_EQ("", content);
+
+  // It should fail gracefully when write fails.
+  script = "write_value(\"value\", \"/proc/0/file1\")";
+  expect("", script.c_str(), kNoCause);
+}
+
+TEST_F(UpdaterTest, get_stage) {
+  // get_stage() expects one argument.
+  expect(nullptr, "get_stage()", kArgsParsingFailure);
+  expect(nullptr, "get_stage(\"arg1\", \"arg2\")", kArgsParsingFailure);
+  expect(nullptr, "get_stage(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+  // Set up a local file as BCB.
+  TemporaryFile tf;
+  std::string temp_file(tf.path);
+  bootloader_message boot;
+  strlcpy(boot.stage, "2/3", sizeof(boot.stage));
+  std::string err;
+  ASSERT_TRUE(write_bootloader_message_to(boot, temp_file, &err));
+
+  // Can read the stage value.
+  std::string script("get_stage(\"" + temp_file + "\")");
+  expect("2/3", script.c_str(), kNoCause);
+
+  // Bad BCB path.
+  script = "get_stage(\"doesntexist\")";
+  expect("", script.c_str(), kNoCause);
+}
+
+TEST_F(UpdaterTest, set_stage) {
+  // set_stage() expects two arguments.
+  expect(nullptr, "set_stage()", kArgsParsingFailure);
+  expect(nullptr, "set_stage(\"arg1\")", kArgsParsingFailure);
+  expect(nullptr, "set_stage(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+  // Set up a local file as BCB.
+  TemporaryFile tf;
+  std::string temp_file(tf.path);
+  bootloader_message boot;
+  strlcpy(boot.command, "command", sizeof(boot.command));
+  strlcpy(boot.stage, "2/3", sizeof(boot.stage));
+  std::string err;
+  ASSERT_TRUE(write_bootloader_message_to(boot, temp_file, &err));
+
+  // Write with set_stage().
+  std::string script("set_stage(\"" + temp_file + "\", \"1/3\")");
+  expect(tf.path, script.c_str(), kNoCause);
+
+  // Verify.
+  bootloader_message boot_verify;
+  ASSERT_TRUE(read_bootloader_message_from(&boot_verify, temp_file, &err));
+
+  // Stage should be updated, with command part untouched.
+  ASSERT_STREQ("1/3", boot_verify.stage);
+  ASSERT_STREQ(boot.command, boot_verify.command);
+
+  // Bad BCB path.
+  script = "set_stage(\"doesntexist\", \"1/3\")";
+  expect("", script.c_str(), kNoCause);
+
+  script = "set_stage(\"/dev/full\", \"1/3\")";
+  expect("", script.c_str(), kNoCause);
+}
diff --git a/update_verifier/Android.mk b/update_verifier/Android.mk
index c822999..8449c75 100644
--- a/update_verifier/Android.mk
+++ b/update_verifier/Android.mk
@@ -26,7 +26,7 @@
     libhardware \
     liblog
 
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
 LOCAL_CFLAGS := -Werror
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/..
 
 include $(BUILD_EXECUTABLE)
diff --git a/updater/Android.mk b/updater/Android.mk
index 3c1d0d4..5d328a3 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -27,12 +27,14 @@
     libedify \
     libziparchive \
     libotautil \
+    libbootloader_message \
     libutils \
     libmounts \
     libotafault \
     libext4_utils_static \
     libfec \
     libfec_rs \
+    libfs_mgr \
     liblog \
     libselinux \
     libsparse_static \
diff --git a/updater/install.cpp b/updater/install.cpp
index b885f86..b9bc19e 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -39,11 +39,12 @@
 #include <string>
 #include <vector>
 
-#include <android-base/parseint.h>
+#include <android-base/file.h>
 #include <android-base/parsedouble.h>
+#include <android-base/parseint.h>
 #include <android-base/properties.h>
-#include <android-base/strings.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <cutils/android_reboot.h>
 #include <ext4_utils/make_ext4fs.h>
 #include <ext4_utils/wipe.h>
@@ -453,28 +454,32 @@
     return StringValue(frac_str);
 }
 
-// package_extract_dir(package_path, destination_path)
-Value* PackageExtractDirFn(const char* name, State* state,
-                          int argc, Expr* argv[]) {
-    if (argc != 2) {
-        return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
-    }
+// package_extract_dir(package_dir, dest_dir)
+//   Extracts all files from the package underneath package_dir and writes them to the
+//   corresponding tree beneath dest_dir. Any existing files are overwritten.
+//   Example: package_extract_dir("system", "/system")
+//
+//   Note: package_dir needs to be a relative path; dest_dir needs to be an absolute path.
+Value* PackageExtractDirFn(const char* name, State* state, int argc, Expr* argv[]) {
+  if (argc != 2) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
+  }
 
-    std::vector<std::string> args;
-    if (!ReadArgs(state, 2, argv, &args)) {
-        return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
-    }
-    const std::string& zip_path = args[0];
-    const std::string& dest_path = args[1];
+  std::vector<std::string> args;
+  if (!ReadArgs(state, 2, argv, &args)) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
+  }
+  const std::string& zip_path = args[0];
+  const std::string& dest_path = args[1];
 
-    ZipArchiveHandle za = ((UpdaterInfo*)(state->cookie))->package_zip;
+  ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip;
 
-    // To create a consistent system image, never use the clock for timestamps.
-    struct utimbuf timestamp = { 1217592000, 1217592000 };  // 8/1/2008 default
+  // To create a consistent system image, never use the clock for timestamps.
+  constexpr struct utimbuf timestamp = { 1217592000, 1217592000 };  // 8/1/2008 default
 
-    bool success = ExtractPackageRecursive(za, zip_path, dest_path, &timestamp, sehandle);
+  bool success = ExtractPackageRecursive(za, zip_path, dest_path, &timestamp, sehandle);
 
-    return StringValue(success ? "t" : "");
+  return StringValue(success ? "t" : "");
 }
 
 // package_extract_file(package_file[, dest_file])
@@ -861,7 +866,6 @@
     return StringValue(value);
 }
 
-
 // file_getprop(file, key)
 //
 //   interprets 'file' as a getprop-style file (key=value pairs, one
@@ -1155,6 +1159,33 @@
     return v;
 }
 
+// write_value(value, filename)
+//   Writes 'value' to 'filename'.
+//   Example: write_value("960000", "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq")
+Value* WriteValueFn(const char* name, State* state, int argc, Expr* argv[]) {
+  if (argc != 2) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
+  }
+
+  std::vector<std::string> args;
+  if (!ReadArgs(state, 2, argv, &args)) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name);
+  }
+
+  const std::string& filename = args[1];
+  if (filename.empty()) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s(): Filename cannot be empty", name);
+  }
+
+  const std::string& value = args[0];
+  if (!android::base::WriteStringToFile(value, filename)) {
+    printf("%s: Failed to write to \"%s\": %s\n", name, filename.c_str(), strerror(errno));
+    return StringValue("");
+  } else {
+    return StringValue("t");
+  }
+}
+
 // Immediately reboot the device.  Recovery is not finished normally,
 // so if you reboot into recovery it will re-start applying the
 // current package (because nothing has cleared the copy of the
@@ -1165,31 +1196,35 @@
 // partition, or "" (empty string) to boot from the regular boot
 // partition.
 Value* RebootNowFn(const char* name, State* state, int argc, Expr* argv[]) {
-    if (argc != 2) {
-        return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
-    }
+  if (argc != 2) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
+  }
 
-    std::vector<std::string> args;
-    if (!ReadArgs(state, 2, argv, &args)) {
-        return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
-    }
-    const std::string& filename = args[0];
-    const std::string& property = args[1];
+  std::vector<std::string> args;
+  if (!ReadArgs(state, 2, argv, &args)) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name);
+  }
+  const std::string& filename = args[0];
+  const std::string& property = args[1];
 
-    // zero out the 'command' field of the bootloader message.
-    char buffer[80];
-    memset(buffer, 0, sizeof(((struct bootloader_message*)0)->command));
-    FILE* f = ota_fopen(filename.c_str(), "r+b");
-    fseek(f, offsetof(struct bootloader_message, command), SEEK_SET);
-    ota_fwrite(buffer, sizeof(((struct bootloader_message*)0)->command), 1, f);
-    ota_fclose(f);
+  // Zero out the 'command' field of the bootloader message. Leave the rest intact.
+  bootloader_message boot;
+  std::string err;
+  if (!read_bootloader_message_from(&boot, filename, &err)) {
+    printf("%s(): Failed to read from \"%s\": %s", name, filename.c_str(), err.c_str());
+    return StringValue("");
+  }
+  memset(boot.command, 0, sizeof(boot.command));
+  if (!write_bootloader_message_to(boot, filename, &err)) {
+    printf("%s(): Failed to write to \"%s\": %s", name, filename.c_str(), err.c_str());
+    return StringValue("");
+  }
 
-    std::string reboot_cmd = "reboot,";
-    reboot_cmd += property;
-    android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd);
+  const std::string reboot_cmd = "reboot," + property;
+  android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd);
 
-    sleep(5);
-    return ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name);
+  sleep(5);
+  return ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name);
 }
 
 // Store a string value somewhere that future invocations of recovery
@@ -1203,62 +1238,57 @@
 // second argument is the string to store; it should not exceed 31
 // bytes.
 Value* SetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
-    if (argc != 2) {
-        return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
-    }
+  if (argc != 2) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %d", name, argc);
+  }
 
-    std::vector<std::string> args;
-    if (!ReadArgs(state, 2, argv, &args)) {
-        return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
-    }
-    const std::string& filename = args[0];
-    std::string& stagestr = args[1];
+  std::vector<std::string> args;
+  if (!ReadArgs(state, 2, argv, &args)) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
+  }
+  const std::string& filename = args[0];
+  const std::string& stagestr = args[1];
 
-    // Store this value in the misc partition, immediately after the
-    // bootloader message that the main recovery uses to save its
-    // arguments in case of the device restarting midway through
-    // package installation.
-    FILE* f = ota_fopen(filename.c_str(), "r+b");
-    fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET);
-    size_t to_write = stagestr.size();
-    size_t max_size = sizeof(((struct bootloader_message*)0)->stage);
-    if (to_write > max_size) {
-        to_write = max_size;
-        stagestr = stagestr.substr(0, max_size-1);
-    }
-    size_t status = ota_fwrite(stagestr.c_str(), to_write, 1, f);
-    ota_fclose(f);
+  // Store this value in the misc partition, immediately after the
+  // bootloader message that the main recovery uses to save its
+  // arguments in case of the device restarting midway through
+  // package installation.
+  bootloader_message boot;
+  std::string err;
+  if (!read_bootloader_message_from(&boot, filename, &err)) {
+    printf("%s(): Failed to read from \"%s\": %s", name, filename.c_str(), err.c_str());
+    return StringValue("");
+  }
+  strlcpy(boot.stage, stagestr.c_str(), sizeof(boot.stage));
+  if (!write_bootloader_message_to(boot, filename, &err)) {
+    printf("%s(): Failed to write to \"%s\": %s", name, filename.c_str(), err.c_str());
+    return StringValue("");
+  }
 
-    if (status != to_write) {
-        return StringValue("");
-    }
-    return StringValue(filename);
+  return StringValue(filename);
 }
 
 // Return the value most recently saved with SetStageFn.  The argument
 // is the block device for the misc partition.
 Value* GetStageFn(const char* name, State* state, int argc, Expr* argv[]) {
-    if (argc != 1) {
-        return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc);
-    }
+  if (argc != 1) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() expects 1 arg, got %d", name, argc);
+  }
 
-    std::vector<std::string> args;
-    if (!ReadArgs(state, 1, argv, &args)) {
-        return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
-    }
-    const std::string& filename = args[0];
+  std::vector<std::string> args;
+  if (!ReadArgs(state, 1, argv, &args)) {
+    return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
+  }
+  const std::string& filename = args[0];
 
-    char buffer[sizeof(((struct bootloader_message*)0)->stage)];
-    FILE* f = ota_fopen(filename.c_str(), "rb");
-    fseek(f, offsetof(struct bootloader_message, stage), SEEK_SET);
-    size_t status = ota_fread(buffer, sizeof(buffer), 1, f);
-    ota_fclose(f);
-    if (status != sizeof(buffer)) {
-        return StringValue("");
-    }
+  bootloader_message boot;
+  std::string err;
+  if (!read_bootloader_message_from(&boot, filename, &err)) {
+    printf("%s(): Failed to read from \"%s\": %s", name, filename.c_str(), err.c_str());
+    return StringValue("");
+  }
 
-    buffer[sizeof(buffer)-1] = '\0';
-    return StringValue(buffer);
+  return StringValue(boot.stage);
 }
 
 Value* WipeBlockDeviceFn(const char* name, State* state, int argc, Expr* argv[]) {
@@ -1363,6 +1393,7 @@
     RegisterFunction("read_file", ReadFileFn);
     RegisterFunction("sha1_check", Sha1CheckFn);
     RegisterFunction("rename", RenameFn);
+    RegisterFunction("write_value", WriteValueFn);
 
     RegisterFunction("wipe_cache", WipeCacheFn);