AU: Class to perform delta updates.

A class to perform delta updates and the associated unittests. Also,
change the delta diff generator executable to be able to apply a
delta, which is handy for debugging.

TEST=Attached unit test, hand-tested on real build images
BUG=552

Review URL: http://codereview.chromium.org/1718001
diff --git a/SConstruct b/SConstruct
index 41dc94f..5c11b6d 100644
--- a/SConstruct
+++ b/SConstruct
@@ -107,7 +107,8 @@
                    bzip_extent_writer.cc
                    cycle_breaker.cc
                    decompressing_file_writer.cc
-                   delta_diff_parser.cc
+                   delta_diff_generator.cc
+                   delta_performer.cc
                    download_action.cc
                    extent_mapper.cc
                    extent_writer.cc
@@ -137,6 +138,7 @@
                             cycle_breaker_unittest.cc
                             decompressing_file_writer_unittest.cc
                             delta_diff_generator_unittest.cc
+                            delta_performer_unittest.cc
                             download_action_unittest.cc
                             extent_mapper_unittest.cc
                             extent_writer_unittest.cc
@@ -159,25 +161,16 @@
                             zip_unittest.cc""")
 unittest_main = ['testrunner.cc']
 
-delta_generator_sources = Split("""delta_diff_generator.cc""")
 delta_generator_main = ['generate_delta_main.cc']
 
-test_installer_main = ['test_installer_main.cc']
-
 env.Program('update_engine', sources + main)
 unittest_cmd = env.Program('update_engine_unittests',
-                           sources + delta_generator_sources +
-                           unittest_sources + unittest_main)
-
-test_installer_cmd = env.Program('test_installer',
-                                 sources + delta_generator_sources +
-                                 unittest_sources + test_installer_main)
+                           sources + unittest_sources + unittest_main)
 
 Clean(unittest_cmd, Glob('*.gcda') + Glob('*.gcno') + Glob('*.gcov') +
                     Split('html app.info'))
 
 delta_generator_cmd = env.Program('delta_generator',
-                                  sources + delta_generator_sources +
-                                  delta_generator_main)
+                                  sources + delta_generator_main)
 
 http_server_cmd = env.Program('test_http_server', 'test_http_server.cc')
diff --git a/bzip_extent_writer.cc b/bzip_extent_writer.cc
index cdf0ce3..e200e2d 100644
--- a/bzip_extent_writer.cc
+++ b/bzip_extent_writer.cc
@@ -14,7 +14,7 @@
 
 bool BzipExtentWriter::Init(int fd,
                             const vector<Extent>& extents,
-                            uint32 block_size) {
+                            uint32_t block_size) {
   // Init bzip2 stream
   int rc = BZ2_bzDecompressInit(&stream_,
                                 0,  // verbosity. (0 == silent)
diff --git a/bzip_extent_writer.h b/bzip_extent_writer.h
index fdd9671..acffbac 100644
--- a/bzip_extent_writer.h
+++ b/bzip_extent_writer.h
@@ -23,7 +23,7 @@
   }
   ~BzipExtentWriter() {}
 
-  bool Init(int fd, const std::vector<Extent>& extents, uint32 block_size);
+  bool Init(int fd, const std::vector<Extent>& extents, uint32_t block_size);
   bool Write(const void* bytes, size_t count);
   bool EndImpl();
 
diff --git a/bzip_extent_writer_unittest.cc b/bzip_extent_writer_unittest.cc
index 3f12c58..d724bc4 100644
--- a/bzip_extent_writer_unittest.cc
+++ b/bzip_extent_writer_unittest.cc
@@ -21,7 +21,7 @@
 
 namespace {
 const char kPathTemplate[] = "./BzipExtentWriterTest-file.XXXXXX";
-const uint32 kBlockSize = 4096;
+const uint32_t kBlockSize = 4096;
 }
 
 class BzipExtentWriterTest : public ::testing::Test {
diff --git a/cycle_breaker.cc b/cycle_breaker.cc
index f6ed31f..6e06898 100644
--- a/cycle_breaker.cc
+++ b/cycle_breaker.cc
@@ -76,7 +76,7 @@
   stack_.push_back(current_vertex_);
   CHECK_GE(stack_.size(), 2);
   Edge min_edge = make_pair(stack_[0], stack_[1]);
-  uint64 min_edge_weight = kuint64max;
+  uint64_t min_edge_weight = kuint64max;
   for (vector<Vertex::Index>::const_iterator it = stack_.begin();
        it != (stack_.end() - 1); ++it) {
     Edge edge = make_pair(*it, *(it + 1));
@@ -84,7 +84,7 @@
       stack_.pop_back();
       return;
     }
-    uint64 edge_weight = graph_utils::EdgeWeight(subgraph_, edge);
+    uint64_t edge_weight = graph_utils::EdgeWeight(subgraph_, edge);
     if (edge_weight < min_edge_weight) {
       min_edge_weight = edge_weight;
       min_edge = edge;
diff --git a/cycle_breaker_unittest.cc b/cycle_breaker_unittest.cc
index ec7f8a3..47a6e75 100644
--- a/cycle_breaker_unittest.cc
+++ b/cycle_breaker_unittest.cc
@@ -72,7 +72,7 @@
 
 namespace {
 pair<Vertex::Index, EdgeProperties> EdgeWithWeight(Vertex::Index dest,
-uint64 weight) {
+uint64_t weight) {
   EdgeProperties props;
   props.extents.resize(1);
   props.extents[0].set_num_blocks(weight);
diff --git a/delta_diff_generator.cc b/delta_diff_generator.cc
index 74bcaa0..6b880cd 100644
--- a/delta_diff_generator.cc
+++ b/delta_diff_generator.cc
@@ -38,9 +38,7 @@
 
 namespace {
 const size_t kBlockSize = 4096;
-const char* const kBsdiffPath = "/usr/bin/bsdiff";
-const uint64 kVersionNumber = 1;
-const char* const kDeltaMagic = "CrAU";
+const uint64_t kVersionNumber = 1;
 
 // Stores all Extents for a file into 'out'. Returns true on success.
 bool GatherExtents(const string& path,
@@ -378,10 +376,10 @@
   return true;
 }
 
-// Writes the uint64 passed in in host-endian to the file as big-endian.
+// Writes the uint64_t passed in in host-endian to the file as big-endian.
 // Returns true on success.
-bool WriteUint64AsBigEndian(FileWriter* writer, const uint64 value) {
-  uint64 value_be = htobe64(value);
+bool WriteUint64AsBigEndian(FileWriter* writer, const uint64_t value) {
+  uint64_t value_be = htobe64(value);
   TEST_AND_RETURN_FALSE(writer->Write(&value_be, sizeof(value_be)) ==
                         sizeof(value_be));
   return true;
@@ -492,13 +490,13 @@
     const vector<Extent>& remove_extents,
     const vector<Extent>& replace_extents) {
   // First, expand out the blocks that op reads from
-  vector<uint64> read_blocks;
+  vector<uint64_t> read_blocks;
   for (int i = 0; i < op->src_extents_size(); i++) {
     const Extent& extent = op->src_extents(i);
     if (extent.start_block() == kSparseHole) {
       read_blocks.resize(read_blocks.size() + extent.num_blocks(), kSparseHole);
     } else {
-      for (uint64 block = extent.start_block();
+      for (uint64_t block = extent.start_block();
            block < (extent.start_block() + extent.num_blocks()); block++) {
         read_blocks.push_back(block);
       }
@@ -506,28 +504,28 @@
   }
   {
     // Expand remove_extents and replace_extents
-    vector<uint64> remove_extents_expanded;
+    vector<uint64_t> remove_extents_expanded;
     for (vector<Extent>::const_iterator it = remove_extents.begin();
          it != remove_extents.end(); ++it) {
       const Extent& extent = *it;
-      for (uint64 block = extent.start_block();
+      for (uint64_t block = extent.start_block();
            block < (extent.start_block() + extent.num_blocks()); block++) {
         remove_extents_expanded.push_back(block);
       }
     }
-    vector<uint64> replace_extents_expanded;
+    vector<uint64_t> replace_extents_expanded;
     for (vector<Extent>::const_iterator it = replace_extents.begin();
          it != replace_extents.end(); ++it) {
       const Extent& extent = *it;
-      for (uint64 block = extent.start_block();
+      for (uint64_t block = extent.start_block();
            block < (extent.start_block() + extent.num_blocks()); block++) {
         replace_extents_expanded.push_back(block);
       }
     }
     CHECK_EQ(remove_extents_expanded.size(), replace_extents_expanded.size());
-    for (vector<uint64>::size_type i = 0;
+    for (vector<uint64_t>::size_type i = 0;
          i < replace_extents_expanded.size(); i++) {
-      vector<uint64>::size_type index = 0;
+      vector<uint64_t>::size_type index = 0;
       CHECK(utils::VectorIndexOf(read_blocks,
                                  remove_extents_expanded[i],
                                  &index));
@@ -538,7 +536,7 @@
   // Convert read_blocks back to extents
   op->clear_src_extents();
   vector<Extent> new_extents;
-  for (vector<uint64>::const_iterator it = read_blocks.begin();
+  for (vector<uint64_t>::const_iterator it = read_blocks.begin();
        it != read_blocks.end(); ++it) {
     graph_utils::AppendBlockToExtents(&new_extents, *it);
   }
@@ -653,7 +651,7 @@
                   O_WRONLY | O_TRUNC | O_CREAT,
                   0644) == 0);
   ScopedFileWriterCloser writer_closer(&writer);
-  uint64 out_file_size = 0;
+  uint64_t out_file_size = 0;
   
   for (int i = 0; i < manifest->install_operations_size(); i++) {
     DeltaArchiveManifest_InstallOperation* op =
@@ -691,7 +689,6 @@
 
   vector<Block> blocks(min(old_image_stbuf.st_size / kBlockSize,
                            new_image_stbuf.st_size / kBlockSize));
-  LOG(INFO) << "w:" << blocks[4097].writer;
   LOG(INFO) << "invalid: " << Vertex::kInvalidIndex;
   LOG(INFO) << "len: " << blocks.size();
   for (vector<Block>::size_type i = 0; i < blocks.size(); i++) {
@@ -781,14 +778,14 @@
 
   // Check that install op blobs are in order and that all blocks are written.
   {
-    vector<uint32> written_count(blocks.size(), 0);
-    uint64 next_blob_offset = 0;
+    vector<uint32_t> written_count(blocks.size(), 0);
+    uint64_t next_blob_offset = 0;
     for (int i = 0; i < manifest.install_operations_size(); i++) {
       const DeltaArchiveManifest_InstallOperation& op =
           manifest.install_operations(i);
       for (int j = 0; j < op.dst_extents_size(); j++) {
         const Extent& extent = op.dst_extents(j);
-        for (uint64 block = extent.start_block();
+        for (uint64_t block = extent.start_block();
              block < (extent.start_block() + extent.num_blocks()); block++) {
           written_count[block]++;
         }
@@ -802,7 +799,7 @@
       }
     }
     // check all blocks written to
-    for (vector<uint32>::size_type i = 0; i < written_count.size(); i++) {
+    for (vector<uint32_t>::size_type i = 0; i < written_count.size(); i++) {
       if (written_count[i] == 0) {
         LOG(FATAL) << "block " << i << " not written!";
       }
@@ -843,7 +840,7 @@
   
   // Append the data blobs
   LOG(INFO) << "Writing final delta file data blobs...";
-  int blobs_fd = open(temp_file_path.c_str(), O_RDONLY, 0);
+  int blobs_fd = open(ordered_blobs_path.c_str(), O_RDONLY, 0);
   ScopedFdCloser blobs_fd_closer(&blobs_fd);
   TEST_AND_RETURN_FALSE(blobs_fd >= 0);
   for (;;) {
@@ -861,4 +858,8 @@
   return true;
 }
 
+const char* const kBsdiffPath = "/usr/bin/bsdiff";
+const char* const kBspatchPath = "/usr/bin/bspatch";
+const char* const kDeltaMagic = "CrAU";
+
 };  // namespace chromeos_update_engine
diff --git a/delta_diff_generator.h b/delta_diff_generator.h
index 6e48519..bbaa473 100644
--- a/delta_diff_generator.h
+++ b/delta_diff_generator.h
@@ -111,6 +111,10 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(DeltaDiffGenerator);
 };
 
+extern const char* const kBsdiffPath;
+extern const char* const kBspatchPath;
+extern const char* const kDeltaMagic;
+
 };  // namespace chromeos_update_engine
 
 #endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_DELTA_DIFF_GENERATOR_H__
diff --git a/delta_diff_generator_unittest.cc b/delta_diff_generator_unittest.cc
index 644d9d8..50f1180 100644
--- a/delta_diff_generator_unittest.cc
+++ b/delta_diff_generator_unittest.cc
@@ -14,6 +14,7 @@
 #include "chromeos/obsolete_logging.h"
 #include "update_engine/cycle_breaker.h"
 #include "update_engine/delta_diff_generator.h"
+#include "update_engine/delta_performer.h"
 #include "update_engine/graph_types.h"
 #include "update_engine/graph_utils.h"
 #include "update_engine/subprocess.h"
@@ -30,9 +31,9 @@
 typedef DeltaDiffGenerator::Block Block;
 
 namespace {
-int64 BlocksInExtents(
+int64_t BlocksInExtents(
     const google::protobuf::RepeatedPtrField<Extent>& extents) {
-  int64 ret = 0;
+  int64_t ret = 0;
   for (int i = 0; i < extents.size(); i++) {
     ret += extents.Get(i).num_blocks();
   }
@@ -139,14 +140,14 @@
 }
 
 namespace {
-void AppendExtent(vector<Extent>* vect, uint64 start, uint64 length) {
+void AppendExtent(vector<Extent>* vect, uint64_t start, uint64_t length) {
   vect->resize(vect->size() + 1);
   vect->back().set_start_block(start);
   vect->back().set_num_blocks(length);
 }
 void OpAppendExtent(DeltaArchiveManifest_InstallOperation* op,
-                    uint64 start,
-                    uint64 length) {
+                    uint64_t start,
+                    uint64_t length) {
   Extent* extent = op->add_src_extents();
   extent->set_start_block(start);
   extent->set_num_blocks(length);
diff --git a/delta_diff_parser.cc b/delta_diff_parser.cc
deleted file mode 100644
index fe7c974..0000000
--- a/delta_diff_parser.cc
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "update_engine/delta_diff_parser.h"
-
-namespace chromeos_update_engine {
-
-}  // namespace chromeos_update_engine
diff --git a/delta_diff_parser.h b/delta_diff_parser.h
deleted file mode 100644
index e6d0b1a..0000000
--- a/delta_diff_parser.h
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_DELTA_DIFF_PARSER_H__
-#define CHROMEOS_PLATFORM_UPDATE_ENGINE_DELTA_DIFF_PARSER_H__
-
-#include "base/basictypes.h"
-
-namespace chromeos_update_engine {
-
-};  // namespace chromeos_update_engine
-
-#endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_DELTA_DIFF_PARSER_H__
diff --git a/delta_diff_parser_unittest.cc b/delta_diff_parser_unittest.cc
deleted file mode 100644
index 241a834..0000000
--- a/delta_diff_parser_unittest.cc
+++ /dev/null
@@ -1,2 +0,0 @@
-// Due to shared code, DeltaDiffParser is tested in
-// delta_diff_generator_unittest.cc
diff --git a/delta_performer.cc b/delta_performer.cc
new file mode 100644
index 0000000..79aea61
--- /dev/null
+++ b/delta_performer.cc
@@ -0,0 +1,376 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/delta_performer.h"
+#include <endian.h>
+#include <errno.h>
+#include <algorithm>
+#include <cstring>
+#include <string>
+#include <vector>
+
+#include <google/protobuf/repeated_field.h>
+
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "update_engine/bzip_extent_writer.h"
+#include "update_engine/delta_diff_generator.h"
+#include "update_engine/extent_writer.h"
+#include "update_engine/graph_types.h"
+#include "update_engine/subprocess.h"
+
+using std::min;
+using std::string;
+using std::vector;
+using google::protobuf::RepeatedPtrField;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+const int kDeltaVersionLength = 8;
+const int kDeltaProtobufLengthLength = 8;
+
+// Remove count bytes from the beginning of *buffer.
+void RemoveBufferHeadBytes(vector<char>* buffer, size_t count) {
+  buffer->erase(buffer->begin(), buffer->begin() + count);
+}
+
+// Converts extents to a human-readable string, for use by DumpUpdateProto().
+string ExtentsToString(const RepeatedPtrField<Extent>& extents) {
+  string ret;
+  for (int i = 0; i < extents.size(); i++) {
+    const Extent& extent = extents.Get(i);
+    if (extent.start_block() == kSparseHole) {
+      ret += StringPrintf("{kSparseHole, %" PRIu64 "}, ", extent.num_blocks());
+    } else {
+      ret += StringPrintf("{%" PRIu64 ", %" PRIu64 "}, ",
+                          extent.start_block(), extent.num_blocks());
+    }
+  }
+  if (!ret.empty()) {
+    DCHECK_GT(ret.size(), static_cast<size_t>(1));
+    ret.resize(ret.size() - 2);
+  }
+  return ret;
+}
+
+// LOGs a DeltaArchiveManifest object. Useful for debugging.
+void DumpUpdateProto(const DeltaArchiveManifest& manifest) {
+  LOG(INFO) << "Update Proto:";
+  LOG(INFO) << "  src_checksum: " << manifest.src_checksum();
+  LOG(INFO) << "  dst_checksum: " << manifest.dst_checksum();
+  LOG(INFO) << "  block_size: " << manifest.block_size();
+  for (int i = 0; i < manifest.install_operations_size(); i++) {
+    const DeltaArchiveManifest_InstallOperation& op =
+        manifest.install_operations(i);
+    LOG(INFO) << "  operation(" << i << ")";
+    LOG(INFO) << "    type: "
+              << DeltaArchiveManifest_InstallOperation_Type_Name(op.type());
+    if (op.has_data_offset())
+      LOG(INFO) << "    data_offset: " << op.data_offset();
+    if (op.has_data_length())
+      LOG(INFO) << "    data_length: " << op.data_length();
+    LOG(INFO) << "    src_extents: " << ExtentsToString(op.src_extents());
+    if (op.has_src_length())
+      LOG(INFO) << "    src_length: " << op.src_length();
+    LOG(INFO) << "    dst_extents: " << ExtentsToString(op.dst_extents());
+    if (op.has_dst_length())
+      LOG(INFO) << "    dst_length: " << op.dst_length();
+  }
+}
+}  // namespace {}
+
+int DeltaPerformer::Open(const char* path, int flags, mode_t mode) {
+  if (fd_ != -1) {
+    LOG(ERROR) << "Can't Open(), fd_ != -1 (it's " << fd_ << ")";
+    return -EINVAL;
+  }
+  path_ = path;
+  fd_ = open(path, O_RDWR, 000);
+  if (fd_ < 0)
+    return -errno;
+  return 0;
+}
+
+int DeltaPerformer::Close() {
+  if (!buffer_.empty()) {
+    LOG(ERROR) << "Called Close() while buffer not empty!";
+    return -1;
+  }
+  if (close(fd_) == -1)
+    return -errno;
+  fd_ = -2;  // Set so that isn't not valid AND calls to Open() will fail.
+  path_ = "";
+  return 0;
+}
+
+// Wrapper around write. Returns bytes written on success or
+// -errno on error.
+// This function performs as many actions as it can, given the amount of
+// data received thus far.
+int DeltaPerformer::Write(const void* bytes, size_t count) {
+  const char* c_bytes = reinterpret_cast<const char*>(bytes);
+  buffer_.insert(buffer_.end(), c_bytes, c_bytes + count);
+
+  if (!manifest_valid_) {
+    // See if we have enough bytes for the manifest yet
+    if (buffer_.size() < strlen(kDeltaMagic) +
+        kDeltaVersionLength + kDeltaProtobufLengthLength) {
+      // Don't have enough bytes to even know the protobuf length
+      return count;
+    }
+    uint64_t protobuf_length;
+    COMPILE_ASSERT(sizeof(protobuf_length) == kDeltaProtobufLengthLength,
+                   protobuf_length_size_mismatch);
+    memcpy(&protobuf_length,
+           &buffer_[strlen(kDeltaMagic) + kDeltaVersionLength],
+           kDeltaProtobufLengthLength);
+    protobuf_length = be64toh(protobuf_length);  // switch big endian to host
+    if (buffer_.size() < strlen(kDeltaMagic) + kDeltaVersionLength +
+        kDeltaProtobufLengthLength + protobuf_length) {
+      return count;
+    }
+    // We have the full proto buffer in buffer_. Parse it.
+    const int offset = strlen(kDeltaMagic) + kDeltaVersionLength +
+        kDeltaProtobufLengthLength;
+    if (!manifest_.ParseFromArray(&buffer_[offset], protobuf_length)) {
+      LOG(ERROR) << "Unable to parse manifest in update file.";
+      return -EINVAL;
+    }
+    // Remove protobuf and header info from buffer_, so buffer_ contains
+    // just data blobs
+    RemoveBufferHeadBytes(&buffer_,
+                          strlen(kDeltaMagic) +
+                          kDeltaVersionLength +
+                          kDeltaProtobufLengthLength + protobuf_length);
+    manifest_valid_ = true;
+    block_size_ = manifest_.block_size();
+  }
+  while (next_operation_ < manifest_.install_operations_size() &&
+         CanPerformInstallOperation(
+             manifest_.install_operations(next_operation_))) {
+    const DeltaArchiveManifest_InstallOperation &op =
+        manifest_.install_operations(next_operation_);
+    if (op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
+        op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
+      if (!PerformReplaceOperation(op)) {
+        LOG(ERROR) << "Failed to perform replace operation " << next_operation_;
+        return -EINVAL;
+      }
+    } else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
+      if (!PerformMoveOperation(op)) {
+        LOG(ERROR) << "Failed to perform move operation " << next_operation_;
+        return -EINVAL;
+      }
+    } else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF) {
+      if (!PerformBsdiffOperation(op)) {
+        LOG(ERROR) << "Failed to perform bsdiff operation " << next_operation_;
+        return -EINVAL;
+      }
+    }
+    next_operation_++;
+  }
+  return count;
+}
+
+bool DeltaPerformer::CanPerformInstallOperation(
+    const chromeos_update_engine::DeltaArchiveManifest_InstallOperation&
+    operation) {
+  // Move operations don't require any data blob, so they can always
+  // be performed
+  if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE)
+    return true;
+
+  // See if we have the entire data blob in the buffer
+  if (operation.data_offset() < buffer_offset_) {
+    LOG(ERROR) << "we threw away data it seems?";
+    return false;
+  }
+  
+  return (operation.data_offset() + operation.data_length()) <=
+      (buffer_offset_ + buffer_.size());
+}
+
+bool DeltaPerformer::PerformReplaceOperation(
+    const DeltaArchiveManifest_InstallOperation& operation) {
+  CHECK(operation.type() == \
+        DeltaArchiveManifest_InstallOperation_Type_REPLACE || \
+        operation.type() == \
+        DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
+
+  // Since we delete data off the beginning of the buffer as we use it,
+  // the data we need should be exactly at the beginning of the buffer.
+  CHECK_EQ(buffer_offset_, operation.data_offset());
+  CHECK_GE(buffer_.size(), operation.data_length());
+  
+  DirectExtentWriter direct_writer;
+  ZeroPadExtentWriter zero_pad_writer(&direct_writer);
+  scoped_ptr<BzipExtentWriter> bzip_writer;
+  
+  // Since bzip decompression is optional, we have a variable writer that will
+  // point to one of the ExtentWriter objects above.
+  ExtentWriter* writer = NULL;
+  if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
+    writer = &zero_pad_writer;
+  } else if (operation.type() ==
+             DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
+    bzip_writer.reset(new BzipExtentWriter(&zero_pad_writer));
+    writer = bzip_writer.get();
+  } else {
+    NOTREACHED();
+  }
+
+  // Create a vector of extents to pass to the ExtentWriter.
+  vector<Extent> extents;
+  for (int i = 0; i < operation.dst_extents_size(); i++) {
+    extents.push_back(operation.dst_extents(i));
+  }
+
+  TEST_AND_RETURN_FALSE(writer->Init(fd_, extents, block_size_));
+  TEST_AND_RETURN_FALSE(writer->Write(&buffer_[0], operation.data_length()));
+  TEST_AND_RETURN_FALSE(writer->End());
+  
+  // Update buffer
+  buffer_offset_ += operation.data_length();
+  RemoveBufferHeadBytes(&buffer_, operation.data_length());
+  return true;
+}
+
+bool DeltaPerformer::PerformMoveOperation(
+    const DeltaArchiveManifest_InstallOperation& operation) {
+  // Calculate buffer size. Note, this function doesn't do a sliding
+  // window to copy in case the source and destination blocks overlap.
+  // If we wanted to do a sliding window, we could program the server
+  // to generate deltas that effectively did a sliding window.
+
+  uint64_t blocks_to_read = 0;
+  for (int i = 0; i < operation.src_extents_size(); i++)
+    blocks_to_read += operation.src_extents(i).num_blocks();
+
+  uint64_t blocks_to_write = 0;
+  for (int i = 0; i < operation.dst_extents_size(); i++)
+    blocks_to_write += operation.dst_extents(i).num_blocks();
+
+  DCHECK_EQ(blocks_to_write, blocks_to_read);
+  vector<char> buf(blocks_to_write * block_size_);
+  
+  // Read in bytes.
+  ssize_t bytes_read = 0;
+  for (int i = 0; i < operation.src_extents_size(); i++) {
+    ssize_t bytes_read_this_iteration = 0;
+    const Extent& extent = operation.src_extents(i);
+    TEST_AND_RETURN_FALSE(utils::PReadAll(fd_,
+                                          &buf[bytes_read],
+                                          extent.num_blocks() * block_size_,
+                                          extent.start_block() * block_size_,
+                                          &bytes_read_this_iteration));
+    TEST_AND_RETURN_FALSE(
+        bytes_read_this_iteration ==
+        static_cast<ssize_t>(extent.num_blocks() * block_size_));
+    bytes_read += bytes_read_this_iteration;
+  }
+
+  // Write bytes out.
+  ssize_t bytes_written = 0;
+  for (int i = 0; i < operation.dst_extents_size(); i++) {
+    const Extent& extent = operation.dst_extents(i);
+    TEST_AND_RETURN_FALSE(utils::PWriteAll(fd_,
+                                           &buf[bytes_written],
+                                           extent.num_blocks() * block_size_,
+                                           extent.start_block() * block_size_));
+    bytes_written += extent.num_blocks() * block_size_;
+  }
+  DCHECK_EQ(bytes_written, bytes_read);
+  DCHECK_EQ(bytes_written, static_cast<ssize_t>(buf.size()));
+  return true;
+}
+
+bool DeltaPerformer::ExtentsToBsdiffPositionsString(
+    const RepeatedPtrField<Extent>& extents,
+    uint64_t block_size,
+    uint64_t full_length,
+    string* positions_string) {
+  string ret;
+  uint64_t length = 0;
+  for (int i = 0; i < extents.size(); i++) {
+    Extent extent = extents.Get(i);
+    int64_t start = extent.start_block();
+    uint64_t this_length = min(full_length - length,
+                               extent.num_blocks() * block_size);
+    if (start == static_cast<int64_t>(kSparseHole))
+      start = -1;
+    else
+      start *= block_size;
+    ret += StringPrintf("%" PRIi64 ":%" PRIu64 ",", start, this_length);
+    length += this_length;
+  }
+  TEST_AND_RETURN_FALSE(length == full_length);
+  if (!ret.empty())
+    ret.resize(ret.size() - 1);  // Strip trailing comma off
+  *positions_string = ret;
+  return true;
+}
+
+bool DeltaPerformer::PerformBsdiffOperation(
+    const DeltaArchiveManifest_InstallOperation& operation) {
+  // Since we delete data off the beginning of the buffer as we use it,
+  // the data we need should be exactly at the beginning of the buffer.
+  CHECK_EQ(buffer_offset_, operation.data_offset());
+  CHECK_GE(buffer_.size(), operation.data_length());
+
+  string input_positions;
+  TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.src_extents(),
+                                                       block_size_,
+                                                       operation.src_length(),
+                                                       &input_positions));
+  string output_positions;
+  TEST_AND_RETURN_FALSE(ExtentsToBsdiffPositionsString(operation.dst_extents(),
+                                                       block_size_,
+                                                       operation.dst_length(),
+                                                       &output_positions));
+
+  string temp_filename;
+  TEST_AND_RETURN_FALSE(utils::MakeTempFile("/tmp/au_patch.XXXXXX",
+                                            &temp_filename,
+                                            NULL));
+  ScopedPathUnlinker path_unlinker(temp_filename);
+  {
+    int fd = open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
+    ScopedFdCloser fd_closer(&fd);
+    TEST_AND_RETURN_FALSE(
+        utils::WriteAll(fd, &buffer_[0], operation.data_length()));
+  }
+  vector<string> cmd;
+  cmd.push_back(kBspatchPath);
+  cmd.push_back(path_);
+  cmd.push_back(path_);
+  cmd.push_back(temp_filename);
+  cmd.push_back(input_positions);
+  cmd.push_back(output_positions);
+  int return_code = 0;
+  TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &return_code));
+  TEST_AND_RETURN_FALSE(return_code == 0);
+
+  if (operation.dst_length() % block_size_) {
+    // Zero out rest of final block.
+    // TODO(adlr): build this into bspatch; it's more efficient that way.
+    const Extent& last_extent =
+        operation.dst_extents(operation.dst_extents_size() - 1);
+    const uint64_t end_byte =
+        (last_extent.start_block() + last_extent.num_blocks()) * block_size_;
+    const uint64_t begin_byte =
+        end_byte - (block_size_ - operation.dst_length() % block_size_);
+    vector<char> zeros(end_byte - begin_byte);
+    TEST_AND_RETURN_FALSE(
+        utils::PWriteAll(fd_, &zeros[0], end_byte - begin_byte, begin_byte));
+  }
+
+  // Update buffer.
+  buffer_offset_ += operation.data_length();
+  RemoveBufferHeadBytes(&buffer_, operation.data_length());
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/delta_performer.h b/delta_performer.h
new file mode 100644
index 0000000..1356bae
--- /dev/null
+++ b/delta_performer.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_DELTA_PERFORMER_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_DELTA_PERFORMER_H__
+
+#include <inttypes.h>
+#include <vector>
+#include <google/protobuf/repeated_field.h>
+#include "update_engine/file_writer.h"
+#include "update_engine/update_metadata.pb.h"
+
+namespace chromeos_update_engine {
+
+// This class performs the actions in a delta update synchronously. The delta
+// update itself should be passed in in chunks as it is received.
+
+class DeltaPerformer : public FileWriter {
+ public:
+  DeltaPerformer()
+      : fd_(-1),
+        manifest_valid_(false),
+        next_operation_(0),
+        buffer_offset_(0),
+        block_size_(0) {}
+  // flags and mode ignored. Once Close()d, a DeltaPerformer can't be
+  // Open()ed again.
+  int Open(const char* path, int flags, mode_t mode);
+
+  // Wrapper around write. Returns bytes written on success or
+  // -errno on error.
+  int Write(const void* bytes, size_t count);
+
+  // Wrapper around close. Returns 0 on success or -errno on error.
+  int Close();
+  
+  // Converts an ordered collection of Extent objects which contain data of
+  // length full_length to a comma-separated string. For each Extent, the
+  // string will have the start offset and then the length in bytes.
+  // The length value of the last extent in the string may be short, since
+  // the full length of all extents in the string is capped to full_length.
+  // Also, an extent starting at kSparseHole, appears as -1 in the string.
+  // For example, if the Extents are {1, 1}, {4, 2}, {kSparseHole, 1},
+  // {0, 1}, block_size is 4096, and full_length is 5 * block_size - 13,
+  // the resulting string will be: "4096:4096,16384:8192,-1:4096,0:4083"
+  static bool ExtentsToBsdiffPositionsString(
+      const google::protobuf::RepeatedPtrField<Extent>& extents,
+      uint64_t block_size,
+      uint64_t full_length,
+      std::string* positions_string);
+
+ private:
+  // Returns true if enough of the delta file has been passed via Write()
+  // to be able to perform a given install operation.
+  bool CanPerformInstallOperation(
+      const DeltaArchiveManifest_InstallOperation& operation);
+  
+  // Returns true on success.
+  bool PerformInstallOperation(
+      const DeltaArchiveManifest_InstallOperation& operation);
+  
+  // These perform a specific type of operation and return true on success.
+  bool PerformReplaceOperation(
+      const DeltaArchiveManifest_InstallOperation& operation);
+  bool PerformMoveOperation(
+      const DeltaArchiveManifest_InstallOperation& operation);
+  bool PerformBsdiffOperation(
+      const DeltaArchiveManifest_InstallOperation& operation);
+
+  // File descriptor of open device.
+  int fd_;
+  
+  std::string path_;  // Path that fd_ refers to
+  
+  DeltaArchiveManifest manifest_;
+  bool manifest_valid_;
+  
+  // Index of the next operation to perform in the manifest.
+  int next_operation_;
+
+  // buffer_ is a window of the data that's been downloaded. At first,
+  // it contains the beginning of the download, but after the protobuf
+  // has been downloaded and parsed, it contains a sliding window of
+  // data blobs.
+  std::vector<char> buffer_;
+  // Offset of buffer_ in the binary blobs section of the update.
+  uint64_t buffer_offset_;
+  
+  // The block size (parsed from the manifest).
+  uint32_t block_size_;
+  
+  DISALLOW_COPY_AND_ASSIGN(DeltaPerformer);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_DELTA_PERFORMER_H__
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
new file mode 100755
index 0000000..7ce2cf0
--- /dev/null
+++ b/delta_performer_unittest.cc
@@ -0,0 +1,194 @@
+// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sys/mount.h>
+#include <inttypes.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <google/protobuf/repeated_field.h>
+#include <gtest/gtest.h>
+
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+#include "update_engine/delta_diff_generator.h"
+#include "update_engine/delta_performer.h"
+#include "update_engine/graph_types.h"
+#include "update_engine/test_utils.h"
+#include "update_engine/update_metadata.pb.h"
+#include "update_engine/utils.h"
+
+namespace chromeos_update_engine {
+
+using std::min;
+using std::string;
+using std::vector;
+
+class DeltaPerformerTest : public ::testing::Test { };
+
+TEST(DeltaPerformerTest, ExtentsToByteStringTest) {
+  uint64_t test[] = {1, 1, 4, 2, kSparseHole, 1, 0, 1};
+  COMPILE_ASSERT(arraysize(test) % 2 == 0, array_size_uneven);
+  const uint64_t block_size = 4096;
+  const uint64_t file_length = 5 * block_size - 13;
+
+  google::protobuf::RepeatedPtrField<Extent> extents;
+  for (size_t i = 0; i < arraysize(test); i += 2) {
+    Extent* extent = extents.Add();
+    extent->set_start_block(test[i]);
+    extent->set_num_blocks(test[i + 1]);
+  }
+
+  string expected_output = "4096:4096,16384:8192,-1:4096,0:4083";
+  string actual_output;
+  EXPECT_TRUE(DeltaPerformer::ExtentsToBsdiffPositionsString(extents,
+                                                             block_size,
+                                                             file_length,
+                                                             &actual_output));
+  EXPECT_EQ(expected_output, actual_output);
+}
+
+class ScopedLoopMounter {
+ public:
+  explicit ScopedLoopMounter(const string& file_path, string* mnt_path,
+                             unsigned long flags) {
+    EXPECT_TRUE(utils::MakeTempDirectory("/tmp/mnt.XXXXXX", mnt_path));
+    dir_remover_.reset(new ScopedDirRemover(*mnt_path));
+
+    string loop_dev = GetUnusedLoopDevice();
+    EXPECT_EQ(0, system(StringPrintf("losetup %s %s", loop_dev.c_str(),
+                                     file_path.c_str()).c_str()));
+    loop_releaser_.reset(new ScopedLoopbackDeviceReleaser(loop_dev));
+
+    EXPECT_TRUE(utils::MountFilesystem(loop_dev, *mnt_path, flags));
+    unmounter_.reset(new ScopedFilesystemUnmounter(*mnt_path));
+  }
+ private:
+  scoped_ptr<ScopedDirRemover> dir_remover_;
+  scoped_ptr<ScopedLoopbackDeviceReleaser> loop_releaser_;
+  scoped_ptr<ScopedFilesystemUnmounter> unmounter_;
+};
+
+void CompareFilesByBlock(const string& a_file, const string& b_file) {
+  vector<char> a_data, b_data;
+  EXPECT_TRUE(utils::ReadFile(a_file, &a_data));
+  EXPECT_TRUE(utils::ReadFile(b_file, &b_data));
+
+  EXPECT_EQ(a_data.size(), b_data.size());
+  size_t kBlockSize = 4096;
+  EXPECT_EQ(0, a_data.size() % kBlockSize);
+  for (size_t i = 0; i < a_data.size(); i += kBlockSize) {
+    EXPECT_EQ(0, i % kBlockSize);
+    vector<char> a_sub(&a_data[i], &a_data[i + kBlockSize]);
+    vector<char> b_sub(&b_data[i], &b_data[i + kBlockSize]);
+    EXPECT_TRUE(a_sub == b_sub) << "Block " << (i/kBlockSize) << " differs";
+  }
+}
+
+namespace {
+bool WriteSparseFile(const string& path, off_t size) {
+  int fd = open(path.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
+  TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);
+  ScopedFdCloser fd_closer(&fd);
+  off_t rc = lseek(fd, size + 1, SEEK_SET);
+  TEST_AND_RETURN_FALSE_ERRNO(rc != static_cast<off_t>(-1));
+  int return_code = ftruncate(fd, size);
+  TEST_AND_RETURN_FALSE_ERRNO(return_code == 0);
+  return true;
+}
+}
+
+TEST(DeltaPerformerTest, RunAsRootSmallImageTest) {
+  string a_img, b_img;
+  EXPECT_TRUE(utils::MakeTempFile("/tmp/a_img.XXXXXX", &a_img, NULL));
+  ScopedPathUnlinker a_img_unlinker(a_img);
+  EXPECT_TRUE(utils::MakeTempFile("/tmp/b_img.XXXXXX", &b_img, NULL));
+  ScopedPathUnlinker b_img_unlinker(b_img);
+
+  CreateExtImageAtPath(a_img, NULL);
+  CreateExtImageAtPath(b_img, NULL);
+
+  // Make some changes to the A image.
+  {
+    string a_mnt;
+    ScopedLoopMounter b_mounter(a_img, &a_mnt, 0);
+
+    EXPECT_TRUE(utils::WriteFile(StringPrintf("%s/hardtocompress",
+                                              a_mnt.c_str()).c_str(),
+                                 reinterpret_cast<const char*>(kRandomString),
+                                 sizeof(kRandomString) - 1));
+    // Write 1 MiB of 0xff to try to catch the case where writing a bsdiff
+    // patch fails to zero out the final block.
+    vector<char> ones(1024 * 1024, 0xff);
+    EXPECT_TRUE(utils::WriteFile(StringPrintf("%s/ones",
+                                              a_mnt.c_str()).c_str(),
+                                 &ones[0],
+                                 ones.size()));
+  }
+
+  // Make some changes to the B image.
+  {
+    string b_mnt;
+    ScopedLoopMounter b_mounter(b_img, &b_mnt, 0);
+
+    EXPECT_EQ(0, system(StringPrintf("cp %s/hello %s/hello2", b_mnt.c_str(),
+                                     b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, system(StringPrintf("rm %s/hello", b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, system(StringPrintf("mv %s/hello2 %s/hello", b_mnt.c_str(),
+                                     b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, system(StringPrintf("echo foo > %s/foo",
+                                     b_mnt.c_str()).c_str()));
+    EXPECT_EQ(0, system(StringPrintf("touch %s/emptyfile",
+                                     b_mnt.c_str()).c_str()));
+    EXPECT_TRUE(WriteSparseFile(StringPrintf("%s/fullsparse", b_mnt.c_str()),
+                                1024 * 1024));
+    EXPECT_EQ(0, system(StringPrintf("dd if=/dev/zero of=%s/partsparese bs=1 "
+                                     "seek=4096 count=1",
+                                     b_mnt.c_str()).c_str()));
+    EXPECT_TRUE(utils::WriteFile(StringPrintf("%s/hardtocompress",
+                                              b_mnt.c_str()).c_str(),
+                                 reinterpret_cast<const char*>(kRandomString),
+                                 sizeof(kRandomString)));
+  }
+
+  string delta_path;
+  EXPECT_TRUE(utils::MakeTempFile("/tmp/delta.XXXXXX", &delta_path, NULL));
+  ScopedPathUnlinker delta_path_unlinker(delta_path);
+  {
+    string a_mnt, b_mnt;
+    ScopedLoopMounter a_mounter(a_img, &a_mnt, MS_RDONLY);
+    ScopedLoopMounter b_mounter(b_img, &b_mnt, MS_RDONLY);
+
+    EXPECT_TRUE(DeltaDiffGenerator::GenerateDeltaUpdateFile(a_mnt,
+                                                            a_img,
+                                                            b_mnt,
+                                                            b_img,
+                                                            delta_path));
+  }
+
+  // Read delta into memory.
+  vector<char> delta;
+  EXPECT_TRUE(utils::ReadFile(delta_path, &delta));
+
+  // Update the A image in place.
+  DeltaPerformer performer;
+
+  EXPECT_EQ(0, performer.Open(a_img.c_str(), 0, 0));
+
+  // Write at some number of bytes per operation. Arbitrarily chose 5.
+  const size_t kBytesPerWrite = 5;
+  for (size_t i = 0; i < delta.size(); i += kBytesPerWrite) {
+    size_t count = min(delta.size() - i, kBytesPerWrite);
+    EXPECT_EQ(count, performer.Write(&delta[i], count));
+  }
+
+  // Wrapper around close. Returns 0 on success or -errno on error.
+  EXPECT_EQ(0, performer.Close());
+
+  CompareFilesByBlock("/tmp/a_ref", "/tmp/b_ref");
+}
+
+}  // namespace chromeos_update_engine
diff --git a/extent_mapper.cc b/extent_mapper.cc
index 4af5f5d..e02f5a2 100755
--- a/extent_mapper.cc
+++ b/extent_mapper.cc
@@ -58,14 +58,14 @@
     rc = ioctl(fd, FIBMAP, &block32);
     TEST_AND_RETURN_FALSE_ERRNO(rc == 0);
     
-    const uint64 block = (block32 == 0 ? kSparseHole : block32);
+    const uint64_t block = (block32 == 0 ? kSparseHole : block32);
     
     graph_utils::AppendBlockToExtents(out, block);
   }
   return true;
 }
 
-bool GetFilesystemBlockSize(const std::string& path, uint32* out_blocksize) {
+bool GetFilesystemBlockSize(const std::string& path, uint32_t* out_blocksize) {
   int fd = open(path.c_str(), O_RDONLY, 0);
   TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);
   ScopedFdCloser fd_closer(&fd);
diff --git a/extent_mapper.h b/extent_mapper.h
index b15cf4a..8805393 100755
--- a/extent_mapper.h
+++ b/extent_mapper.h
@@ -26,7 +26,7 @@
 
 // Puts the blocksize of the filesystem, as used by the FIBMAP ioctl, into
 // out_blocksize by using the FIGETBSZ ioctl. Returns true on success.
-bool GetFilesystemBlockSize(const std::string& path, uint32* out_blocksize);
+bool GetFilesystemBlockSize(const std::string& path, uint32_t* out_blocksize);
 
 }  // namespace extent_mapper
 
diff --git a/extent_mapper_unittest.cc b/extent_mapper_unittest.cc
index dc69437..7a5e598 100644
--- a/extent_mapper_unittest.cc
+++ b/extent_mapper_unittest.cc
@@ -29,7 +29,7 @@
   // executable are consistent and they match with the size of the file.
   const string kFilename = "/proc/self/exe";
   
-  uint32 block_size = 0;
+  uint32_t block_size = 0;
   EXPECT_TRUE(extent_mapper::GetFilesystemBlockSize(kFilename, &block_size));
   EXPECT_GT(block_size, 0);
     
@@ -38,11 +38,11 @@
   ASSERT_TRUE(extent_mapper::ExtentsForFileFibmap(kFilename, &extents));
   
   EXPECT_FALSE(extents.empty());
-  set<uint64> blocks;
+  set<uint64_t> blocks;
   
   for (vector<Extent>::const_iterator it = extents.begin();
        it != extents.end(); ++it) {
-    for (uint64 block = it->start_block();
+    for (uint64_t block = it->start_block();
          block < it->start_block() + it->num_blocks();
          block++) {
       EXPECT_FALSE(utils::SetContainsKey(blocks, block));
@@ -68,7 +68,7 @@
   int fd = mkstemp(buf);
   ASSERT_GE(fd, 0);
 
-  uint32 block_size = 0;
+  uint32_t block_size = 0;
   EXPECT_TRUE(extent_mapper::GetFilesystemBlockSize(buf, &block_size));
   EXPECT_GT(block_size, 0);
   
diff --git a/extent_writer.cc b/extent_writer.cc
index 9f6fbf0..baac66c 100644
--- a/extent_writer.cc
+++ b/extent_writer.cc
@@ -6,6 +6,7 @@
 #include <errno.h>
 #include <unistd.h>
 #include <algorithm>
+#include "update_engine/graph_types.h"
 #include "update_engine/utils.h"
 
 using std::min;
@@ -19,12 +20,12 @@
   size_t bytes_written = 0;
   while (count - bytes_written > 0) {
     TEST_AND_RETURN_FALSE(next_extent_index_ < extents_.size());
-    uint64 bytes_remaining_next_extent =
+    uint64_t bytes_remaining_next_extent =
         extents_[next_extent_index_].num_blocks() * block_size_ -
         extent_bytes_written_;
     CHECK_NE(bytes_remaining_next_extent, 0);
     size_t bytes_to_write =
-        static_cast<size_t>(min(static_cast<uint64>(count - bytes_written),
+        static_cast<size_t>(min(static_cast<uint64_t>(count - bytes_written),
                                 bytes_remaining_next_extent));
     TEST_AND_RETURN_FALSE(bytes_to_write > 0);
 
diff --git a/extent_writer.h b/extent_writer.h
index b250bab..ac36364 100644
--- a/extent_writer.h
+++ b/extent_writer.h
@@ -15,10 +15,6 @@
 
 namespace chromeos_update_engine {
 
-// When an extent's start block is kSparseHole, that data written for that
-// extent will be dropped rather than written to the unerlying fd.
-const uint64 kSparseHole = kuint64max;
-
 class ExtentWriter {
  public:
   ExtentWriter() : end_called_(false) {}
@@ -73,7 +69,7 @@
   
   size_t block_size_;
   // Bytes written into next_extent_index_ thus far
-  uint64 extent_bytes_written_;
+  uint64_t extent_bytes_written_;
   std::vector<Extent> extents_;
   // The next call to write should correspond to extents_[next_extent_index_]
   std::vector<Extent>::size_type next_extent_index_;
diff --git a/extent_writer_unittest.cc b/extent_writer_unittest.cc
index a35ba26..484d069 100644
--- a/extent_writer_unittest.cc
+++ b/extent_writer_unittest.cc
@@ -10,6 +10,7 @@
 #include <vector>
 #include <gtest/gtest.h>
 #include "update_engine/extent_writer.h"
+#include "update_engine/graph_types.h"
 #include "update_engine/test_utils.h"
 #include "update_engine/utils.h"
 
diff --git a/file_writer.h b/file_writer.h
index c506c01..37207d0 100644
--- a/file_writer.h
+++ b/file_writer.h
@@ -21,6 +21,7 @@
 
 class FileWriter {
  public:
+  FileWriter() {}
   virtual ~FileWriter() {}
 
   // Wrapper around open. Returns 0 on success or -errno on error.
@@ -32,6 +33,9 @@
 
   // Wrapper around close. Returns 0 on success or -errno on error.
   virtual int Close() = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(FileWriter);
 };
 
 // Direct file writer is probably the simplest FileWriter implementation.
@@ -50,6 +54,8 @@
 
  private:
   int fd_;
+  
+  DISALLOW_COPY_AND_ASSIGN(DirectFileWriter);
 };
 
 class ScopedFileWriterCloser {
@@ -63,6 +69,8 @@
   }
  private:
   FileWriter* writer_;
+  
+  DISALLOW_COPY_AND_ASSIGN(ScopedFileWriterCloser);
 };
 
 }  // namespace chromeos_update_engine
diff --git a/filesystem_copier_action.cc b/filesystem_copier_action.cc
index f9b6869..2a361ee 100644
--- a/filesystem_copier_action.cc
+++ b/filesystem_copier_action.cc
@@ -93,7 +93,7 @@
 bool FilesystemCopierAction::Mount(const string& device,
                                    const string& mountpoint) {
   CHECK(!is_mounted_);
-  if(utils::MountFilesystem(device, mountpoint))
+  if(utils::MountFilesystem(device, mountpoint, 0))
     is_mounted_ = true;
   return is_mounted_;
 }
diff --git a/filesystem_iterator_unittest.cc b/filesystem_iterator_unittest.cc
index da14b87..676d91f 100644
--- a/filesystem_iterator_unittest.cc
+++ b/filesystem_iterator_unittest.cc
@@ -8,8 +8,8 @@
 #include <set>
 #include <string>
 #include <vector>
-#include "base/string_util.h"
 #include <gtest/gtest.h>
+#include "base/string_util.h"
 #include "update_engine/filesystem_iterator.h"
 #include "update_engine/test_utils.h"
 #include "update_engine/utils.h"
diff --git a/generate_delta_main.cc b/generate_delta_main.cc
index 2f21b2a..a996fb8 100644
--- a/generate_delta_main.cc
+++ b/generate_delta_main.cc
@@ -5,14 +5,17 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <unistd.h>
 #include <set>
 #include <string>
+#include <vector>
 #include <gflags/gflags.h>
 #include <glib.h>
 #include "base/command_line.h"
 #include "chromeos/obsolete_logging.h"
 #include "update_engine/delta_diff_generator.h"
+#include "update_engine/delta_performer.h"
 #include "update_engine/subprocess.h"
 #include "update_engine/update_metadata.pb.h"
 #include "update_engine/utils.h"
@@ -24,6 +27,8 @@
 DEFINE_string(old_image, "", "Path to the old rootfs");
 DEFINE_string(new_image, "", "Path to the new rootfs");
 DEFINE_string(out_file, "", "Path to output file");
+DEFINE_string(apply_delta, "",
+              "If set, apply delta over old_image. (For debugging.)");
 
 // This file contains a simple program that takes an old path, a new path,
 // and an output file as arguments and the path to an output file and
@@ -31,6 +36,7 @@
 
 using std::set;
 using std::string;
+using std::vector;
 
 namespace chromeos_update_engine {
 
@@ -51,6 +57,27 @@
                        logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
                        logging::DONT_LOCK_LOG_FILE,
                        logging::APPEND_TO_OLD_LOG_FILE);
+  if (!FLAGS_apply_delta.empty()) {
+    if (FLAGS_old_image.empty()) {
+      LOG(FATAL) << "Must pass --old_image with --apply_delta.";
+    }
+    DeltaPerformer performer;
+    CHECK_EQ(performer.Open(FLAGS_old_image.c_str(), 0, 0), 0);
+    vector<char> buf(1024 * 1024);
+    int fd = open(FLAGS_apply_delta.c_str(), O_RDONLY, 0);
+    CHECK_GE(fd, 0);
+    ScopedFdCloser fd_closer(&fd);
+    for (off_t offset = 0;; offset += buf.size()) {
+      ssize_t bytes_read;
+      CHECK(utils::PReadAll(fd, &buf[0], buf.size(), offset, &bytes_read));
+      if (bytes_read == 0)
+        break;
+      CHECK_EQ(performer.Write(&buf[0], bytes_read), bytes_read);
+    }
+    CHECK_EQ(performer.Close(), 0);
+    LOG(INFO) << "done applying delta.";
+    return 0;
+  }
   if (FLAGS_old_dir.empty() || FLAGS_new_dir.empty() ||
       FLAGS_old_image.empty() || FLAGS_new_image.empty() ||
       FLAGS_out_file.empty()) {
diff --git a/graph_types.h b/graph_types.h
index 109616a..e7867f6 100644
--- a/graph_types.h
+++ b/graph_types.h
@@ -50,7 +50,7 @@
 
 typedef std::pair<Vertex::Index, Vertex::Index> Edge;
 
-const uint64 kSparseHole = kuint64max;
+const uint64_t kSparseHole = kuint64max;
 
 }  // namespace chromeos_update_engine
 
diff --git a/graph_utils.cc b/graph_utils.cc
index dd3cdcf..5ad67cf 100644
--- a/graph_utils.cc
+++ b/graph_utils.cc
@@ -11,8 +11,8 @@
 
 namespace graph_utils {
 
-uint64 EdgeWeight(const Graph& graph, const Edge& edge) {
-  uint64 weight = 0;
+uint64_t EdgeWeight(const Graph& graph, const Edge& edge) {
+  uint64_t weight = 0;
   const vector<Extent>& extents =
       graph[edge.first].out_edges.find(edge.second)->second.extents;
   for (vector<Extent>::const_iterator it = extents.begin();
@@ -23,7 +23,7 @@
   return weight;
 }
 
-void AppendBlockToExtents(vector<Extent>* extents, uint64 block) {
+void AppendBlockToExtents(vector<Extent>* extents, uint64_t block) {
   if (!extents->empty()) {
     Extent& extent = extents->back();
     if (block == kSparseHole) {
diff --git a/graph_utils.h b/graph_utils.h
index 62ecfd5..fd602ea 100644
--- a/graph_utils.h
+++ b/graph_utils.h
@@ -17,12 +17,12 @@
 namespace graph_utils {
 
 // Returns the number of blocks represented by all extents in the edge.
-uint64 EdgeWeight(const Graph& graph, const Edge& edge);
+uint64_t EdgeWeight(const Graph& graph, const Edge& edge);
 
 // block must either be the next block in the last extent or a block
 // in the next extent. This function will not handle inserting block
 // into an arbitrary place in the extents.
-void AppendBlockToExtents(std::vector<Extent>* extents, uint64 block);
+void AppendBlockToExtents(std::vector<Extent>* extents, uint64_t block);
 
 }  // namespace graph_utils
 
diff --git a/omaha_hash_calculator.h b/omaha_hash_calculator.h
index c96f75e..900da80 100644
--- a/omaha_hash_calculator.h
+++ b/omaha_hash_calculator.h
@@ -5,10 +5,10 @@
 #ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_OMAHA_HASH_CALCULATOR_H__
 #define CHROMEOS_PLATFORM_UPDATE_ENGINE_OMAHA_HASH_CALCULATOR_H__
 
-#include "base/basictypes.h"
 #include <string>
 #include <vector>
 #include <openssl/sha.h>
+#include "base/basictypes.h"
 
 // Omaha uses base64 encoded SHA-1 as the hash. This class provides a simple
 // wrapper around OpenSSL providing such a formatted hash of data passed in.
diff --git a/set_bootable_flag_action_unittest.cc b/set_bootable_flag_action_unittest.cc
index 215c189..023c7bd 100644
--- a/set_bootable_flag_action_unittest.cc
+++ b/set_bootable_flag_action_unittest.cc
@@ -51,7 +51,7 @@
   }
   vector<char> DoTest(vector<char> mbr_in,
                       const string& filename,
-                      uint32 test_flags);
+                      uint32_t test_flags);
   // first partition bootable, no others bootable
   const vector<char> sample_mbr_;
 };
@@ -64,7 +64,7 @@
 
 vector<char> SetBootableFlagActionTest::DoTest(vector<char> mbr_in,
                                                const string& filename,
-                                               uint32 flags) {
+                                               uint32_t flags) {
   CHECK(!filename.empty());
   const string root_filename(filename.begin(), --filename.end());
   if (flags & WRITE_FILE)
diff --git a/subprocess.cc b/subprocess.cc
index 3a6597e..87d8253 100644
--- a/subprocess.cc
+++ b/subprocess.cc
@@ -17,7 +17,7 @@
 namespace chromeos_update_engine {
 
 void Subprocess::GChildExitedCallback(GPid pid, gint status, gpointer data) {
-  COMPILE_ASSERT(sizeof(guint) == sizeof(uint32),
+  COMPILE_ASSERT(sizeof(guint) == sizeof(uint32_t),
                  guint_uint32_size_mismatch);
   guint* tag = reinterpret_cast<guint*>(data);
   const SubprocessCallbackRecord& record = Get().callback_records_[*tag];
@@ -37,7 +37,7 @@
 }
 }  // namespace {}
 
-uint32 Subprocess::Exec(const std::vector<std::string>& cmd,
+uint32_t Subprocess::Exec(const std::vector<std::string>& cmd,
                         ExecCallback callback,
                         void* p) {
   GPid child_pid;
@@ -79,7 +79,7 @@
   return *tag;
 }
 
-void Subprocess::CancelExec(uint32 tag) {
+void Subprocess::CancelExec(uint32_t tag) {
   if (callback_records_[tag].callback) {
     callback_records_[tag].callback = NULL;
   }
diff --git a/subprocess.h b/subprocess.h
index 92064a6..7fd3052 100644
--- a/subprocess.h
+++ b/subprocess.h
@@ -9,8 +9,8 @@
 #include <string>
 #include <vector>
 #include <glib.h>
-#include "chromeos/obsolete_logging.h"
 #include "base/basictypes.h"
+#include "chromeos/obsolete_logging.h"
 
 // The Subprocess class is a singleton. It's used to spawn off a subprocess
 // and get notified when the subprocess exits. The result of Exec() can
@@ -29,12 +29,12 @@
   typedef void(*ExecCallback)(int return_code, void *p);
 
   // Returns a tag > 0 on success.
-  uint32 Exec(const std::vector<std::string>& cmd,
-              ExecCallback callback,
-              void* p);
+  uint32_t Exec(const std::vector<std::string>& cmd,
+                ExecCallback callback,
+                void* p);
 
   // Used to cancel the callback. The process will still run to completion.
-  void CancelExec(uint32 tag);
+  void CancelExec(uint32_t tag);
 
   // Executes a command synchronously. Returns true on success.
   static bool SynchronousExec(const std::vector<std::string>& cmd,
diff --git a/subprocess_unittest.cc b/subprocess_unittest.cc
index 430f39b..86fba01 100644
--- a/subprocess_unittest.cc
+++ b/subprocess_unittest.cc
@@ -62,7 +62,7 @@
   CancelTestData* cancel_test_data = reinterpret_cast<CancelTestData*>(data);
   vector<string> cmd;
   cmd.push_back("./test_http_server");
-  uint32 tag = Subprocess::Get().Exec(cmd, CallbackBad, NULL);
+  uint32_t tag = Subprocess::Get().Exec(cmd, CallbackBad, NULL);
   EXPECT_NE(0, tag);
   cancel_test_data->spawned = true;
   printf("spawned\n");
diff --git a/test_installer_main.cc b/test_installer_main.cc
deleted file mode 100644
index 435f5d4..0000000
--- a/test_installer_main.cc
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// TODO(adlr): get rid of commented out lines or comment them back in.
-// Look for "// re-add" next to those comments.
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <set>
-#include <string>
-#include <vector>
-
-#include <gflags/gflags.h>
-#include <glib.h>
-#include "chromeos/obsolete_logging.h"
-
-#include "update_engine/delta_diff_generator.h"
-#include "update_engine/delta_diff_parser.h"
-#include "update_engine/filesystem_copier_action.h"
-// #include "update_engine/install_action.h"  // re-add
-#include "update_engine/install_plan.h"
-#include "update_engine/test_utils.h"
-#include "update_engine/update_metadata.pb.h"
-#include "update_engine/utils.h"
-
-// This file contains a simple program that unpacks a delta diff into a
-// directory.
-
-using std::set;
-using std::string;
-using std::vector;
-using std::tr1::shared_ptr;
-
-DEFINE_string(delta, "", "the delta file");
-DEFINE_string(output_dev, "", "the output device");
-DEFINE_string(root, "", "the fake system root");
-DEFINE_string(dump_delta, "", "path to delta file to dump");
-
-namespace chromeos_update_engine {
-
-class TestInstaller : public ActionProcessorDelegate {
- public:
-  TestInstaller(GMainLoop *loop,
-                const string& sys_root,
-                const string& delta_path,
-                const string& install_path)
-      : loop_(loop),
-        sys_root_(sys_root),
-        install_path_(install_path) {}
-  void Update();
-
-  // Delegate method:
-  void ProcessingDone(const ActionProcessor* processor, bool success);
- private:
-  vector<shared_ptr<AbstractAction> > actions_;
-  ActionProcessor processor_;
-  GMainLoop *loop_;
-  string sys_root_;
-  string install_path_;
-};
-
-// Returns true on success. If there was no update available, that's still
-// success.
-// If force_full is true, try to force a full update.
-void TestInstaller::Update() {
-  CHECK(!processor_.IsRunning());
-  processor_.set_delegate(this);
-
-  // Actions:
-  shared_ptr<ObjectFeederAction<InstallPlan> > object_feeder_action(
-      new ObjectFeederAction<InstallPlan>);
-  shared_ptr<FilesystemCopierAction> filesystem_copier_action(
-      new FilesystemCopierAction);
-  // shared_ptr<InstallAction> install_action(  // re-add
-  //     new InstallAction);
-
-  actions_.push_back(object_feeder_action);
-  actions_.push_back(filesystem_copier_action);
-  // actions_.push_back(install_action);  // re-add
-
-  // Enqueue the actions
-  for (vector<shared_ptr<AbstractAction> >::iterator it = actions_.begin();
-       it != actions_.end(); ++it) {
-    processor_.EnqueueAction(it->get());
-  }
-
-  // Bond them together. We have to use the leaf-types when calling
-  // BondActions().
-  BondActions(object_feeder_action.get(), filesystem_copier_action.get());
-  // re-add
-  // BondActions(filesystem_copier_action.get(), install_action.get());
-  
-  InstallPlan install_plan(false,
-                           "",
-                           "",
-                           install_path_);
-  filesystem_copier_action->set_copy_source(sys_root_);
-  object_feeder_action->set_obj(install_plan);
-  processor_.StartProcessing();
-}
-
-void TestInstaller::ProcessingDone(const ActionProcessor* processor,
-                                   bool success) {
-  LOG(INFO) << "install " << (success ? "succeeded" : "failed");
-  actions_.clear();
-  g_main_loop_quit(loop_);
-}
-
-gboolean TestInstallInMainLoop(void* arg) {
-  TestInstaller* test_installer = reinterpret_cast<TestInstaller*>(arg);
-  test_installer->Update();
-  return FALSE;  // Don't call this callback function again
-}
-
-
-void usage(const char* argv0) {
-  printf("usage: %s --root=system_root --delta=delta_file "
-         "--output_dev=output_dev\n", argv0);
-  exit(1);
-}
-
-bool Main(int argc, char** argv) {
-  g_thread_init(NULL);
-  google::ParseCommandLineFlags(&argc, &argv, true);
-  logging::InitLogging("",
-                       logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
-                       logging::DONT_LOCK_LOG_FILE,
-                       logging::APPEND_TO_OLD_LOG_FILE);
-
-  LOG(INFO) << "starting";
-  
-  // Create the single GMainLoop
-  GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
-
-  chromeos_update_engine::TestInstaller test_installer(loop,
-                                                       FLAGS_root,
-                                                       FLAGS_delta,
-                                                       FLAGS_output_dev);
-
-  g_timeout_add(0, &chromeos_update_engine::TestInstallInMainLoop,
-                &test_installer);
-
-  g_main_loop_run(loop);
-  g_main_loop_unref(loop);
-
-  LOG(INFO) << "Done.";
-  return true;
-}
-
-}  // namespace chromeos_update_engine
-
-int main(int argc, char** argv) {
-  return chromeos_update_engine::Main(argc, argv) ? 0 : 1;
-}
diff --git a/test_utils.cc b/test_utils.cc
index 6a86025..851e45d 100644
--- a/test_utils.cc
+++ b/test_utils.cc
@@ -3,18 +3,20 @@
 // found in the LICENSE file.
 
 #include "update_engine/test_utils.h"
+
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+
 #include <set>
 #include <string>
 #include <vector>
+
 #include "base/string_util.h"
 #include "chromeos/obsolete_logging.h"
-
 #include "update_engine/file_writer.h"
 #include "update_engine/filesystem_iterator.h"
 #include "update_engine/utils.h"
@@ -190,7 +192,7 @@
   EXPECT_EQ(0, System(StringPrintf("dd if=/dev/zero of=%s"
                                    " seek=10485759 bs=1 count=1",
                                    path.c_str())));
-  EXPECT_EQ(0, System(StringPrintf("mkfs.ext3 -F %s", path.c_str())));
+  EXPECT_EQ(0, System(StringPrintf("mkfs.ext3 -b 4096 -F %s", path.c_str())));
   EXPECT_EQ(0, System(StringPrintf("mkdir -p %s", kMountPath)));
   EXPECT_EQ(0, System(StringPrintf("mount -o loop %s %s", path.c_str(),
                                    kMountPath)));
diff --git a/test_utils.h b/test_utils.h
index bba3ed1..2a87f49 100644
--- a/test_utils.h
+++ b/test_utils.h
@@ -10,6 +10,7 @@
 #include <vector>
 #include <gtest/gtest.h>
 #include "update_engine/action.h"
+#include "update_engine/subprocess.h"
 
 // These are some handy functions for unittests.
 
@@ -105,6 +106,23 @@
 void VerifyAllPaths(const std::string& parent,
                     std::set<std::string> expected_paths);
 
+class ScopedLoopbackDeviceReleaser {
+ public:
+  explicit ScopedLoopbackDeviceReleaser(const std::string& dev) : dev_(dev) {}
+  ~ScopedLoopbackDeviceReleaser() {
+    std::vector<std::string> args;
+    args.push_back("/sbin/losetup");
+    args.push_back("-d");
+    args.push_back(dev_);
+    int return_code = 0;
+    EXPECT_TRUE(Subprocess::SynchronousExec(args, &return_code));
+    EXPECT_EQ(0, return_code);
+  }
+ private:
+  const std::string dev_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedLoopbackDeviceReleaser);
+};
+
 // Useful actions for test
 
 class NoneType;
diff --git a/update_check_action.h b/update_check_action.h
index 5622a63..da48aba 100644
--- a/update_check_action.h
+++ b/update_check_action.h
@@ -5,8 +5,8 @@
 #ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_CHECK_ACTION_H__
 #define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_CHECK_ACTION_H__
 
-#include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/types.h>
 #include <fcntl.h>
 
 #include <string>
@@ -14,8 +14,8 @@
 #include <curl/curl.h>
 
 #include "base/scoped_ptr.h"
-#include "action.h"
-#include "http_fetcher.h"
+#include "update_engine/action.h"
+#include "update_engine/http_fetcher.h"
 
 // The Update Check action makes an update check request to Omaha and
 // can output the response on the output ActionPipe.
diff --git a/utils.cc b/utils.cc
index ff3c9db..7f05ffb 100644
--- a/utils.cc
+++ b/utils.cc
@@ -35,7 +35,7 @@
   return true;
 }
 
-bool WriteAll(int fd, const void *buf, size_t count) {
+bool WriteAll(int fd, const void* buf, size_t count) {
   const char* c_buf = static_cast<const char*>(buf);
   ssize_t bytes_written = 0;
   while (bytes_written < static_cast<ssize_t>(count)) {
@@ -46,6 +46,36 @@
   return true;
 }
 
+bool PWriteAll(int fd, const void* buf, size_t count, off_t offset) {
+  const char* c_buf = static_cast<const char*>(buf);
+  ssize_t bytes_written = 0;
+  while (bytes_written < static_cast<ssize_t>(count)) {
+    ssize_t rc = pwrite(fd, c_buf + bytes_written, count - bytes_written,
+                        offset + bytes_written);
+    TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
+    bytes_written += rc;
+  }
+  return true;
+}
+
+bool PReadAll(int fd, void* buf, size_t count, off_t offset,
+              ssize_t* out_bytes_read) {
+  char* c_buf = static_cast<char*>(buf);
+  ssize_t bytes_read = 0;
+  while (bytes_read < static_cast<ssize_t>(count)) {
+    ssize_t rc = pread(fd, c_buf + bytes_read, count - bytes_read,
+                       offset + bytes_read);
+    TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
+    if (rc == 0) {
+      break;
+    }
+    bytes_read += rc;
+  }
+  *out_bytes_read = bytes_read;
+  return true;
+  
+}
+
 bool ReadFile(const std::string& path, std::vector<char>* out) {
   CHECK(out);
   FILE* fp = fopen(path.c_str(), "r");
@@ -233,6 +263,19 @@
   return true;
 }
 
+bool MakeTempDirectory(const std::string& dirname_template,
+                       std::string* dirname) {
+  DCHECK(dirname);
+  vector<char> buf(dirname_template.size() + 1);
+  memcpy(&buf[0], dirname_template.data(), dirname_template.size());
+  buf[dirname_template.size()] = '\0';
+  
+  char* return_code = mkdtemp(&buf[0]);
+  TEST_AND_RETURN_FALSE_ERRNO(return_code != NULL);
+  *dirname = &buf[0];
+  return true;
+}
+
 bool StringHasSuffix(const std::string& str, const std::string& suffix) {
   if (suffix.size() > str.size())
     return false;
@@ -273,8 +316,9 @@
 }
 
 bool MountFilesystem(const string& device,
-                     const string& mountpoint) {
-  int rc = mount(device.c_str(), mountpoint.c_str(), "ext3", 0, NULL);
+                     const string& mountpoint,
+                     unsigned long mountflags) {
+  int rc = mount(device.c_str(), mountpoint.c_str(), "ext3", mountflags, NULL);
   if (rc < 0) {
     string msg = ErrnoNumberAsString(errno);
     LOG(ERROR) << "Unable to mount destination device: " << msg << ". "
diff --git a/utils.h b/utils.h
index 298fb03..bcbd8da 100644
--- a/utils.h
+++ b/utils.h
@@ -5,6 +5,7 @@
 #ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UTILS_H__
 #define CHROMEOS_PLATFORM_UPDATE_ENGINE_UTILS_H__
 
+#include <errno.h>
 #include <algorithm>
 #include <set>
 #include <string>
@@ -20,9 +21,15 @@
 // exists. Returns true on success, false otherwise.
 bool WriteFile(const char* path, const char* data, int data_len);
 
-// Calls write() repeatedly until all count bytes at buf are written to
-// fd or an error occurs. Returns true on success.
-bool WriteAll(int fd, const void *buf, size_t count);
+// Calls write() or pwrite() repeatedly until all count bytes at buf are
+// written to fd or an error occurs. Returns true on success.
+bool WriteAll(int fd, const void* buf, size_t count);
+bool PWriteAll(int fd, const void* buf, size_t count, off_t offset);
+
+// Calls pread() repeatedly until count bytes are read, or EOF is reached.
+// Returns number of bytes read in *bytes_read. Returns true on success.
+bool PReadAll(int fd, void* buf, size_t count, off_t offset,
+              ssize_t* out_bytes_read);
 
 // Returns the entire contents of the file at path. Returns true on success.
 bool ReadFile(const std::string& path, std::vector<char>* out);
@@ -55,6 +62,11 @@
                   std::string* filename,
                   int* fd);
 
+// Calls mkdtemp() with the tempate passed. Returns the generated dirname
+// in the dirname param. Returns TRUE on success. dirname must not be NULL.
+bool MakeTempDirectory(const std::string& dirname_template,
+                       std::string* dirname);
+
 // Deletes a directory and all its contents synchronously. Returns true
 // on success. This may be called with a regular file--it will just unlink it.
 // This WILL cross filesystem boundaries.
@@ -62,7 +74,8 @@
 
 // Synchronously mount or unmount a filesystem. Return true on success.
 // Mounts as ext3 with default options.
-bool MountFilesystem(const std::string& device, const std::string& mountpoint);
+bool MountFilesystem(const std::string& device, const std::string& mountpoint,
+                     unsigned long flags);
 bool UnmountFilesystem(const std::string& mountpoint);
 
 // Log a string in hex to LOG(INFO). Useful for debugging.
@@ -131,6 +144,7 @@
   }
  private:
   const std::string mountpoint_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedFilesystemUnmounter);
 };
 
 // Utility class to close a file descriptor
@@ -149,6 +163,35 @@
  private:
   int* fd_;
   bool should_close_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedFdCloser);
+};
+
+// Utility class to delete a file when it goes out of scope.
+class ScopedPathUnlinker {
+ public:
+  explicit ScopedPathUnlinker(const std::string& path) : path_(path) {}
+  ~ScopedPathUnlinker() {
+    if (unlink(path_.c_str()) < 0) {
+      std::string err_message = strerror(errno);
+      LOG(ERROR) << "Unable to unlink path " << path_ << ": " << err_message;
+    }
+  }
+ private:
+  const std::string path_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedPathUnlinker);
+};
+
+// Utility class to delete an empty directory when it goes out of scope.
+class ScopedDirRemover {
+ public:
+  explicit ScopedDirRemover(const std::string& path) : path_(path) {}
+  ~ScopedDirRemover() {
+    if (rmdir(path_.c_str()) < 0)
+      PLOG(ERROR) << "Unable to remove dir " << path_;
+  }
+ private:
+  const std::string path_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedDirRemover);
 };
 
 // A little object to call ActionComplete on the ActionProcessor when