Merge "applypatch: Use unique_fd to avoid leaking FDs." am: 2e5cf3c0bc
am: d2cd8b4d30

Change-Id: I57228be65dfc37f853c3902ad8c5f65c0e3f1018
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index 5483a68..9b84fa1 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -194,62 +194,59 @@
 // 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::vector<std::string> pieces = android::base::Split(std::string(target), ":");
-
+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);
+    printf("WriteToPartition called with bad target (%s)\n", target.c_str());
     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) {
+  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));
@@ -268,23 +265,23 @@
     }
 
     if (ota_fsync(fd) != 0) {
-      printf("failed to sync to %s (%s)\n", partition, strerror(errno));
+      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));
+      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));
+    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();
-    int dc = ota_open("/proc/sys/vm/drop_caches", O_WRONLY);
+    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 {
@@ -293,7 +290,7 @@
     ota_close(dc);
     sleep(1);
 
-    // verify
+    // 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;
@@ -318,8 +315,7 @@
           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));
+          printf("short verify read %s at %zu: %zd %zu\n", partition, p, read_count, to_read);
         }
         so_far += read_count;
       }
@@ -343,8 +339,8 @@
     return -1;
   }
 
-  if (ota_close(fd) != 0) {
-    printf("error closing %s (%s)\n", partition, strerror(errno));
+  if (ota_close(fd) == -1) {
+    printf("error closing %s: %s\n", partition, strerror(errno));
     return -1;
   }
   sync();
@@ -586,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;
+  }
 
-    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, ':');
-    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;
-    }
+  // 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,
@@ -641,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/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