AU: Try delta updates first, then full updates

Also, some bug fixes.

Review URL: http://codereview.chromium.org/492008
diff --git a/SConstruct b/SConstruct
index 64406e4..0d124d9 100644
--- a/SConstruct
+++ b/SConstruct
@@ -123,16 +123,24 @@
 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)
+
 Clean(unittest_cmd, Glob('*.gcda') + Glob('*.gcno') + Glob('*.gcov') +
                     Split('html app.info'))
 
 delta_generator_cmd = env.Program('delta_generator',
-                                  sources + delta_generator_sources + main)
+                                  sources + delta_generator_sources +
+                                  delta_generator_main)
 
 http_server_cmd = env.Program('test_http_server', 'test_http_server.cc')
diff --git a/action.h b/action.h
index af6deee..b45100a 100644
--- a/action.h
+++ b/action.h
@@ -62,8 +62,6 @@
 // consulting ActionTraits<T>::InputObjectType (and OutputObjectType).
 // This is why the ActionTraits classes are needed.
 
-using std::tr1::shared_ptr;
-
 namespace chromeos_update_engine {
 
 // It is handy to have a non-templated base class of all Actions.
@@ -111,7 +109,7 @@
   // std::type_info for this?
   // Type() returns a string of the Action type. I.e., for DownloadAction,
   // Type() would return "DownloadAction".
-  virtual string Type() const = 0;
+  virtual std::string Type() const = 0;
 
  protected:
   // A weak pointer to the processor that owns this Action.
@@ -138,7 +136,7 @@
   void set_in_pipe(
       // this type is a fancy way of saying: a shared_ptr to an
       // ActionPipe<InputObjectType>.
-      const shared_ptr<ActionPipe<
+      const std::tr1::shared_ptr<ActionPipe<
           typename ActionTraits<SubClass>::InputObjectType> >&
           in_pipe) {
     in_pipe_ = in_pipe;
@@ -151,7 +149,7 @@
   void set_out_pipe(
       // this type is a fancy way of saying: a shared_ptr to an
       // ActionPipe<OutputObjectType>.
-      const shared_ptr<ActionPipe<
+      const std::tr1::shared_ptr<ActionPipe<
           typename ActionTraits<SubClass>::OutputObjectType> >&
           out_pipe) {
     out_pipe_ = out_pipe;
@@ -194,9 +192,11 @@
   // point to when the last such shared_ptr object dies. We consider the
   // Actions on either end of a pipe to "own" the pipe. When the last Action
   // of the two dies, the ActionPipe will die, too.
-  shared_ptr<ActionPipe<typename ActionTraits<SubClass>::InputObjectType> >
+  std::tr1::shared_ptr<
+      ActionPipe<typename ActionTraits<SubClass>::InputObjectType> >
       in_pipe_;
-  shared_ptr<ActionPipe<typename ActionTraits<SubClass>::OutputObjectType> >
+  std::tr1::shared_ptr<
+      ActionPipe<typename ActionTraits<SubClass>::OutputObjectType> >
       out_pipe_;
 };
 
diff --git a/action_pipe.h b/action_pipe.h
index 509fbee..7697043 100644
--- a/action_pipe.h
+++ b/action_pipe.h
@@ -29,10 +29,6 @@
 // the two Action objects. a shared_ptr is used so that when the last Action
 // pointing to an ActionPipe dies, the ActionPipe dies, too.
 
-using std::map;
-using std::string;
-using std::tr1::shared_ptr;
-
 namespace chromeos_update_engine {
 
 // Used by Actions an InputObjectType or OutputObjectType to specify that
@@ -62,7 +58,8 @@
   // when the last Action is destroyed.
   template<typename FromAction, typename ToAction>
   static void Bond(FromAction* from, ToAction* to) {
-    shared_ptr<ActionPipe<ObjectType> > pipe(new ActionPipe<ObjectType>);
+    std::tr1::shared_ptr<ActionPipe<ObjectType> > pipe(
+        new ActionPipe<ObjectType>);
     from->set_out_pipe(pipe);
 
     to->set_in_pipe(pipe);  // If you get an error on this line, then
diff --git a/action_pipe_unittest.cc b/action_pipe_unittest.cc
index 28cf248..b0a96b7 100644
--- a/action_pipe_unittest.cc
+++ b/action_pipe_unittest.cc
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <string>
 #include <gtest/gtest.h>
 #include "update_engine/action.h"
 #include "update_engine/action_pipe.h"
 
+using std::string;
+
 namespace chromeos_update_engine {
 
 using chromeos_update_engine::ActionPipe;
diff --git a/action_processor.cc b/action_processor.cc
index 21a88f1..d58b5ee 100644
--- a/action_processor.cc
+++ b/action_processor.cc
@@ -3,9 +3,12 @@
 // found in the LICENSE file.
 
 #include "update_engine/action_processor.h"
+#include <string>
 #include "chromeos/obsolete_logging.h"
 #include "update_engine/action.h"
 
+using std::string;
+
 namespace chromeos_update_engine {
 
 ActionProcessor::ActionProcessor()
@@ -70,7 +73,7 @@
     LOG(INFO) << "ActionProcessor::ActionComplete: finished last action of"
                  " type " << old_type;
     if (delegate_) {
-      delegate_->ProcessingDone(this);
+      delegate_->ProcessingDone(this, success);
     }
     return;
   }
diff --git a/action_processor.h b/action_processor.h
index 938a81d..8311b8a 100644
--- a/action_processor.h
+++ b/action_processor.h
@@ -78,8 +78,9 @@
 class ActionProcessorDelegate {
  public:
   // Called when all processing in an ActionProcessor has completed. A pointer
-  // to the ActionProcessor is passed.
-  virtual void ProcessingDone(const ActionProcessor* processor) {}
+  // to the ActionProcessor is passed. success is true iff all actions
+  // completed successfully
+  virtual void ProcessingDone(const ActionProcessor* processor, bool success) {}
 
   // Called when processing has stopped. Does not mean that all Actions have
   // completed. If/when all Actions complete, ProcessingDone() will be called.
diff --git a/action_processor_unittest.cc b/action_processor_unittest.cc
index 6130585..0657207 100644
--- a/action_processor_unittest.cc
+++ b/action_processor_unittest.cc
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <string>
 #include <gtest/gtest.h>
 #include "update_engine/action.h"
 #include "update_engine/action_processor.h"
 
+using std::string;
+
 namespace chromeos_update_engine {
 
 using chromeos_update_engine::ActionPipe;
@@ -60,7 +63,7 @@
   explicit MyActionProcessorDelegate(const ActionProcessor* processor)
       : processor_(processor), processing_done_called_(false) {}
 
-  virtual void ProcessingDone(const ActionProcessor* processor) {
+  virtual void ProcessingDone(const ActionProcessor* processor, bool success) {
     EXPECT_EQ(processor_, processor);
     EXPECT_FALSE(processing_done_called_);
     processing_done_called_ = true;
diff --git a/action_unittest.cc b/action_unittest.cc
index 371f84d..8dd9edd 100644
--- a/action_unittest.cc
+++ b/action_unittest.cc
@@ -2,10 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <string>
 #include <gtest/gtest.h>
 #include "update_engine/action.h"
 #include "update_engine/action_processor.h"
 
+using std::string;
+
 namespace chromeos_update_engine {
 
 using chromeos_update_engine::ActionPipe;
diff --git a/delta_diff_generator.cc b/delta_diff_generator.cc
index 45872bc..2dacfdf 100644
--- a/delta_diff_generator.cc
+++ b/delta_diff_generator.cc
@@ -9,6 +9,9 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <algorithm>
+#include <map>
+#include <set>
+#include <string>
 #include <vector>
 #include <tr1/memory>
 #include <zlib.h>
@@ -19,6 +22,9 @@
 #include "update_engine/subprocess.h"
 #include "update_engine/utils.h"
 
+using std::map;
+using std::set;
+using std::string;
 using std::vector;
 using std::tr1::shared_ptr;
 using chromeos_update_engine::DeltaArchiveManifest;
@@ -26,6 +32,9 @@
 namespace chromeos_update_engine {
 
 namespace {
+
+const char* kBsdiffPath = "/usr/bin/bsdiff";
+
 // These structs and methods are helpers for EncodeDataToDeltaFile()
 
 // Before moving the data into a proto buffer, the data is stored in
@@ -200,12 +209,13 @@
 bool DeltaDiffGenerator::WriteFileDiffsToDeltaFile(
     DeltaArchiveManifest* archive,
     DeltaArchiveManifest_File* file,
-    const std::string& file_name,
-    const std::string& old_path,
-    const std::string& new_path,
+    const string& file_name,
+    const string& old_path,
+    const string& new_path,
     FileWriter* out_file_writer,
     int* out_file_length,
-    const std::string& force_compress_dev_path) {
+    set<string> always_full_target_paths,
+    const string& force_compress_dev_path) {
   TEST_AND_RETURN_FALSE(file->has_mode());
 
   // Stat the actual file, too
@@ -220,14 +230,21 @@
       DeltaArchiveManifest_File_Child* child = file->mutable_children(i);
       DeltaArchiveManifest_File* child_file =
           archive->mutable_files(child->index());
+      string recurse_old_path = old_path;
+      string recurse_new_path = new_path;
+      if (!file_name.empty()) {
+        recurse_new_path += "/" + file_name;
+        recurse_old_path += "/" + file_name;
+      }
       TEST_AND_RETURN_FALSE(WriteFileDiffsToDeltaFile(
           archive,
           child_file,
           child->name(),
-          old_path + "/" + file_name,
-          new_path + "/" + file_name,
+          recurse_old_path,
+          recurse_new_path,
           out_file_writer,
           out_file_length,
+          always_full_target_paths,
           force_compress_dev_path));
     }
     return true;
@@ -252,8 +269,15 @@
     format_set = true;
   } else if (S_ISREG(file->mode())) {
     // regular file. We may use a delta here.
-    TEST_AND_RETURN_FALSE(EncodeFile(old_path, new_path, file_name, &format,
-                                     &data));
+    const bool avoid_diff = utils::SetContainsKey(always_full_target_paths,
+                                                  new_path + "/" + file_name);
+    bool no_change = false;
+    TEST_AND_RETURN_FALSE(EncodeFile(old_path, new_path, file_name,
+                                     avoid_diff, &format, &data, &no_change));
+    if (no_change) {
+      // No data change. We're done!
+      return true;
+    }
     should_compress = false;
     format_set = true;
     if ((format == DeltaArchiveManifest_File_DataFormat_BSDIFF) ||
@@ -278,20 +302,17 @@
     format_set = true;
   }
 
-  if (!data.empty()) {
-    TEST_AND_RETURN_FALSE(format_set);
-    file->set_data_format(format);
-    file->set_data_offset(*out_file_length);
-    TEST_AND_RETURN_FALSE(static_cast<ssize_t>(data.size()) ==
-                          out_file_writer->Write(&data[0], data.size()));
-    file->set_data_length(data.size());
-    *out_file_length += data.size();
-  }
+  TEST_AND_RETURN_FALSE(format_set);
+  file->set_data_format(format);
+  file->set_data_offset(*out_file_length);
+  TEST_AND_RETURN_FALSE(static_cast<ssize_t>(data.size()) ==
+                        out_file_writer->Write(&data[0], data.size()));
+  file->set_data_length(data.size());
+  *out_file_length += data.size();
   return true;
 }
 
-bool DeltaDiffGenerator::EncodeLink(const std::string& path,
-                                    std::vector<char>* out) {
+bool DeltaDiffGenerator::EncodeLink(const string& path, vector<char>* out) {
   // Store symlink path as file data
   vector<char> link_data(4096);
   int rc = readlink(path.c_str(), &link_data[0], link_data.size());
@@ -329,56 +350,85 @@
     const string& old_dir,
     const string& new_dir,
     const string& file_name,
+    const bool avoid_diff,
     DeltaArchiveManifest_File_DataFormat* out_data_format,
-    vector<char>* out) {
+    vector<char>* out,
+    bool* no_change) {
   TEST_AND_RETURN_FALSE(out_data_format);
-  // First, see the full length:
+  vector<char> ret;
   vector<char> full_data;
-  TEST_AND_RETURN_FALSE(utils::ReadFile(new_dir + "/" + file_name, &full_data));
-  vector<char> gz_data;
-  if (!full_data.empty()) {
-    TEST_AND_RETURN_FALSE(GzipCompress(full_data, &gz_data));
-  }
-  vector<char> *ret = NULL;
+  {
+    // First, see the full length:
+    TEST_AND_RETURN_FALSE(utils::ReadFile(new_dir + "/" + file_name,
+                                          &full_data));
+    vector<char> gz_data;
+    if (!full_data.empty()) {
+      TEST_AND_RETURN_FALSE(GzipCompress(full_data, &gz_data));
+    }
 
-  if (gz_data.size() < full_data.size()) {
-    *out_data_format = DeltaArchiveManifest_File_DataFormat_FULL_GZ;
-    ret = &gz_data;
-  } else {
-    *out_data_format = DeltaArchiveManifest_File_DataFormat_FULL;
-    ret = &full_data;
+    if (gz_data.size() < full_data.size()) {
+      *out_data_format = DeltaArchiveManifest_File_DataFormat_FULL_GZ;
+      ret.swap(gz_data);
+    } else {
+      *out_data_format = DeltaArchiveManifest_File_DataFormat_FULL;
+      ret = full_data;
+    }
+  }
+
+  if (avoid_diff) {
+    out->swap(ret);
+    return true;
   }
 
   struct stat old_stbuf;
   if ((stat((old_dir + "/" + file_name).c_str(), &old_stbuf) < 0) ||
       (!S_ISREG(old_stbuf.st_mode))) {
-    // stat() failed or old file is not a regular file. Just send back the full
-    // contents
-    *out = *ret;
+    // stat() failed or old file is not a regular file. Just send back
+    // the full contents
+    out->swap(ret);
     return true;
   }
-  // We have an old file. Do a binary diff. For now use bsdiff.
-  const string kPatchFile = "/tmp/delta.patch";
+  // We have an old file.
+  // First see if the data is _exactly_ the same
+  {
+    vector<char> original_data;
+    TEST_AND_RETURN_FALSE(utils::ReadFile(old_dir + "/" + file_name,
+                                          &original_data));
+    if (original_data == full_data) {
+      // Original data unchanged in new file.
+      *no_change = true;
+      return true;
+    }
+  }
+  
+  // Do a binary diff. For now use bsdiff.
+  const string kPatchFile = "/tmp/delta.patchXXXXXX";
+  vector<char> patch_file_path(kPatchFile.begin(), kPatchFile.end());
+  patch_file_path.push_back('\0');
+  
+  int fd = mkstemp(&patch_file_path[0]);
+  if (fd >= 0)
+    close(fd);
+  TEST_AND_RETURN_FALSE(fd != -1);
 
   vector<string> cmd;
-  cmd.push_back("/usr/bin/bsdiff");
+  cmd.push_back(kBsdiffPath);
   cmd.push_back(old_dir + "/" + file_name);
   cmd.push_back(new_dir + "/" + file_name);
-  cmd.push_back(kPatchFile);
+  cmd.push_back(&patch_file_path[0]);
 
   int rc = 1;
+  vector<char> patch_file;
   TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &rc));
   TEST_AND_RETURN_FALSE(rc == 0);
-  vector<char> patch_file;
-  TEST_AND_RETURN_FALSE(utils::ReadFile(kPatchFile, &patch_file));
-  unlink(kPatchFile.c_str());
+  TEST_AND_RETURN_FALSE(utils::ReadFile(&patch_file_path[0], &patch_file));
+  unlink(&patch_file_path[0]);
 
-  if (patch_file.size() < ret->size()) {
+  if (patch_file.size() < ret.size()) {
     *out_data_format = DeltaArchiveManifest_File_DataFormat_BSDIFF;
-    ret = &patch_file;
+    ret.swap(patch_file);
   }
-
-  *out = *ret;
+  out->swap(ret);
   return true;
 }
 
@@ -397,10 +447,11 @@
 
 bool DeltaDiffGenerator::EncodeDataToDeltaFile(
     DeltaArchiveManifest* archive,
-    const std::string& old_path,
-    const std::string& new_path,
-    const std::string& out_file,
-    const std::string& force_compress_dev_path) {
+    const string& old_path,
+    const string& new_path,
+    const string& out_file,
+    const set<string>& nondiff_paths,
+    const string& force_compress_dev_path) {
   DirectFileWriter out_writer;
   int r = out_writer.Open(out_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
   TEST_AND_RETURN_FALSE_ERRNO(r >= 0);
@@ -419,6 +470,16 @@
   TEST_AND_RETURN_FALSE(archive->files_size() > 0);
   DeltaArchiveManifest_File* file = archive->mutable_files(0);
 
+  // nondiff_paths is passed in w/ paths relative to the installed
+  // system (e.g. /etc/fstab), but WriteFileDiffsToDeltaFile requires them
+  // to be the entire path of the new file. We create a new set
+  // here with nondiff_paths expanded.
+  set<string> always_full_target_paths;
+  for (set<string>::const_iterator it = nondiff_paths.begin();
+       it != nondiff_paths.end(); ++it) {
+    always_full_target_paths.insert(new_path + *it);
+  }
+
   TEST_AND_RETURN_FALSE(WriteFileDiffsToDeltaFile(archive,
                                                   file,
                                                   "",
@@ -426,6 +487,7 @@
                                                   new_path,
                                                   &out_writer,
                                                   &out_file_length,
+                                                  always_full_target_paths,
                                                   force_compress_dev_path));
 
   // Finally, write the protobuf to the end of the file
diff --git a/delta_diff_generator.h b/delta_diff_generator.h
index 2c355a4..5d422c3 100644
--- a/delta_diff_generator.h
+++ b/delta_diff_generator.h
@@ -7,6 +7,7 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <set>
 #include <string>
 #include <vector>
 #include "base/basictypes.h"
@@ -32,14 +33,19 @@
   // Takes a DeltaArchiveManifest as given from EncodeMetadataToProtoBuffer(),
   // fill in the missing fields (DeltaArchiveManifest_File_data_*), and
   // write the full delta out to the output file.
+  // Any paths in nondiff_paths will be included in full, rather than
+  // as a diff. This is useful for files that change during postinstall, since
+  // future updates can't depend on them having remaining unchanged.
   // Returns true on success.
   // If non-empty, the device at force_compress_dev_path will be compressed.
-  static bool EncodeDataToDeltaFile(DeltaArchiveManifest* archive,
-                                    const std::string& old_path,
-                                    const std::string& new_path,
-                                    const std::string& out_file,
-                                    const std::string& force_compress_dev_path);
-
+  static bool EncodeDataToDeltaFile(
+      DeltaArchiveManifest* archive,
+      const std::string& old_path,
+      const std::string& new_path,
+      const std::string& out_file,
+      const std::set<std::string>& nondiff_paths,
+      const std::string& force_compress_dev_path);
+                                    
  private:
   // These functions encode all the data about a file that's not already
   // stored in the DeltaArchiveManifest message into the vector 'out'.
@@ -58,9 +64,15 @@
   static bool EncodeFile(const std::string& old_dir,
                          const std::string& new_dir,
                          const std::string& file_name,
+                         const bool avoid_diff,
                          DeltaArchiveManifest_File_DataFormat* out_data_format,
-                         std::vector<char>* out);
+                         std::vector<char>* out,
+                         bool* no_change);
 
+  // nondiff_paths is passed in to EncodeDataToDeltaFile() with
+  // paths relative to the installed system (e.g. /etc/fstab), but
+  // WriteFileDiffsToDeltaFile requires always_full_target_paths to be
+  // the entire path of the new file.
   // If non-empty, the device at force_compress_dev_path will be compressed.
   static bool WriteFileDiffsToDeltaFile(
       DeltaArchiveManifest* archive,
@@ -70,6 +82,7 @@
       const std::string& new_path,
       FileWriter* out_file_writer,
       int* out_file_length,
+      std::set<std::string> always_full_target_paths,
       const std::string& force_compress_dev_path);
 
   // This should never be constructed
diff --git a/delta_diff_generator_unittest.cc b/delta_diff_generator_unittest.cc
index b549041..f86f0e2 100644
--- a/delta_diff_generator_unittest.cc
+++ b/delta_diff_generator_unittest.cc
@@ -6,6 +6,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <set>
 #include <string>
 #include <vector>
 #include "base/string_util.h"
@@ -22,10 +23,17 @@
 
 namespace chromeos_update_engine {
 
+using std::set;
 using std::string;
 using std::vector;
 
-class DeltaDiffGeneratorTest : public ::testing::Test {};
+class DeltaDiffGeneratorTest : public ::testing::Test {
+  virtual void TearDown() {
+    EXPECT_EQ(0, system("rm -rf diff-gen-test"));
+  }
+protected:
+  void FakerootEncodeDataToDeltaFileTest(bool test_diff_exclusion);
+};
 
 namespace {
 void DumpProto(const DeltaArchiveManifest* archive) {
@@ -350,6 +358,14 @@
 }
 
 TEST_F(DeltaDiffGeneratorTest, FakerootEncodeDataToDeltaFileTest) {
+  FakerootEncodeDataToDeltaFileTest(false);
+}
+TEST_F(DeltaDiffGeneratorTest, FakerootDiffExclusionsTest) {
+  FakerootEncodeDataToDeltaFileTest(true);
+}
+
+void DeltaDiffGeneratorTest::FakerootEncodeDataToDeltaFileTest(
+    bool test_diff_exclusion) {
   char cwd[1000];
   ASSERT_EQ(cwd, getcwd(cwd, sizeof(cwd))) << "cwd buf possibly too small";
   ASSERT_EQ(0, System(string("mkdir -p ") + cwd + "/diff-gen-test"));
@@ -359,6 +375,13 @@
   GenerateFilesAtPath(string(cwd) + "/diff-gen-test/new");
   EditFilesAtPath(string(cwd) + "/diff-gen-test/new");
 
+  set<string> diff_exclusions;
+  if (test_diff_exclusion) {
+    diff_exclusions.insert("/encoding/long_small_change");
+  } else {
+    diff_exclusions.insert("/hi");
+  }
+
   DeltaArchiveManifest* archive =
       DeltaDiffGenerator::EncodeMetadataToProtoBuffer(
           (string(cwd) + "/diff-gen-test/new").c_str());
@@ -369,7 +392,7 @@
       string(cwd) + "/diff-gen-test/old",
       string(cwd) + "/diff-gen-test/new",
       string(cwd) + "/diff-gen-test/out.dat",
-      ""));
+      diff_exclusions, ""));
 
   EXPECT_EQ(18, archive->files_size());
 
@@ -476,8 +499,11 @@
   EXPECT_EQ(0, long_small_change.uid());
   EXPECT_EQ(0, long_small_change.gid());
   EXPECT_TRUE(long_small_change.has_data_format());
-  EXPECT_EQ(DeltaArchiveManifest_File_DataFormat_BSDIFF,
-            long_small_change.data_format());
+  DeltaArchiveManifest_File_DataFormat expected_format =
+      DeltaArchiveManifest_File_DataFormat_BSDIFF;
+  if (test_diff_exclusion)
+    expected_format = DeltaArchiveManifest_File_DataFormat_FULL;
+  EXPECT_EQ(expected_format, long_small_change.data_format());
   EXPECT_TRUE(long_small_change.has_data_offset());
   EXPECT_TRUE(long_small_change.has_data_length());
   EXPECT_FALSE(long_small_change.has_hardlink_path());
@@ -488,10 +514,9 @@
   EXPECT_TRUE(S_ISREG(nochange.mode()));
   EXPECT_EQ(0, nochange.uid());
   EXPECT_EQ(0, nochange.gid());
-  EXPECT_TRUE(nochange.has_data_format());
-  EXPECT_EQ(DeltaArchiveManifest_File_DataFormat_FULL, nochange.data_format());
-  EXPECT_TRUE(nochange.has_data_offset());
-  EXPECT_TRUE(nochange.has_data_length());
+  EXPECT_FALSE(nochange.has_data_format());
+  EXPECT_FALSE(nochange.has_data_offset());
+  EXPECT_FALSE(nochange.has_data_length());
   EXPECT_FALSE(nochange.has_hardlink_path());
 
   const DeltaArchiveManifest_File& onebyte =
@@ -566,9 +591,10 @@
   EXPECT_TRUE(S_ISREG(newempty.mode()));
   EXPECT_EQ(0, newempty.uid());
   EXPECT_EQ(0, newempty.gid());
-  EXPECT_FALSE(newempty.has_data_format());
-  EXPECT_FALSE(newempty.has_data_offset());
-  EXPECT_FALSE(newempty.has_data_length());
+  EXPECT_TRUE(newempty.has_data_format());
+  EXPECT_EQ(DeltaArchiveManifest_File_DataFormat_FULL, newempty.data_format());
+  EXPECT_TRUE(newempty.has_data_offset());
+  EXPECT_TRUE(newempty.has_data_length());
   EXPECT_FALSE(newempty.has_hardlink_path());
 
   const DeltaArchiveManifest_File& subdir =
@@ -667,7 +693,7 @@
       string(cwd) + "/diff-gen-test/old",
       string(cwd) + "/diff-gen-test/new",
       string(cwd) + "/diff-gen-test/out.dat",
-      ""));
+      set<string>(), ""));
   // parse the file
 
   DeltaDiffParser parser(string(cwd) + "/diff-gen-test/out.dat");
@@ -789,7 +815,8 @@
   // newempty
   file = parser.GetFileAtPath("/dir/newempty");
   EXPECT_TRUE(S_ISREG(file.mode()));
-  EXPECT_FALSE(file.has_data_format());
+  EXPECT_TRUE(file.has_data_format());
+  EXPECT_EQ(DeltaArchiveManifest_File_DataFormat_FULL, file.data_format());
 
   // subdir
   file = parser.GetFileAtPath("/dir/subdir");
@@ -845,7 +872,7 @@
   cmd.push_back(string(cwd) + "/diff-gen-test/patch_result");
   cmd.push_back(string(cwd) + "/diff-gen-test/patch");
   Subprocess::SynchronousExec(cmd, &rc);
-  EXPECT_EQ(0, rc);
+  ASSERT_EQ(0, rc);
   vector<char> patch_result;
   EXPECT_TRUE(utils::ReadFile(string(cwd) + "/diff-gen-test/patch_result",
                               &patch_result));
@@ -857,12 +884,9 @@
   // nochange
   file = parser.GetFileAtPath("/encoding/nochange");
   EXPECT_TRUE(S_ISREG(file.mode()));
-  EXPECT_TRUE(file.has_data_format());
-  EXPECT_EQ(DeltaArchiveManifest_File_DataFormat_FULL, file.data_format());
-  EXPECT_EQ("nochange\n", ReadFilePartToString(string(cwd) +
-                                               "/diff-gen-test/out.dat",
-                                               file.data_offset(),
-                                               file.data_length()));
+  EXPECT_FALSE(file.has_data_format());
+  EXPECT_FALSE(file.has_data_offset());
+  EXPECT_FALSE(file.has_data_length());
 
   // onebyte
   file = parser.GetFileAtPath("/encoding/onebyte");
diff --git a/download_action.h b/download_action.h
index e6939e1..d5ec026 100644
--- a/download_action.h
+++ b/download_action.h
@@ -9,7 +9,6 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 
-#include <map>
 #include <string>
 
 #include <curl/curl.h>
@@ -25,9 +24,6 @@
 // The Download Action downloads a requested url to a specified path on disk.
 // The url and output path are determined by the InstallPlan passed in.
 
-using std::map;
-using std::string;
-
 namespace chromeos_update_engine {
 
 class DownloadAction;
diff --git a/download_action_unittest.cc b/download_action_unittest.cc
index 4276dc9..57e8d7e 100644
--- a/download_action_unittest.cc
+++ b/download_action_unittest.cc
@@ -28,7 +28,7 @@
   virtual ~DownloadActionTestProcessorDelegate() {
     EXPECT_TRUE(processing_done_called_);
   }
-  virtual void ProcessingDone(const ActionProcessor* processor) {
+  virtual void ProcessingDone(const ActionProcessor* processor, bool success) {
     ASSERT_TRUE(loop_);
     g_main_loop_quit(loop_);
     vector<char> found_data;
@@ -214,7 +214,7 @@
 // only by the test PassObjectOutTest.
 class PassObjectOutTestProcessorDelegate : public ActionProcessorDelegate {
  public:
-  void ProcessingDone(const ActionProcessor* processor) {
+  void ProcessingDone(const ActionProcessor* processor, bool success) {
     ASSERT_TRUE(loop_);
     g_main_loop_quit(loop_);
   }
diff --git a/filesystem_copier_action.cc b/filesystem_copier_action.cc
index 875df95..f9b6869 100644
--- a/filesystem_copier_action.cc
+++ b/filesystem_copier_action.cc
@@ -9,12 +9,14 @@
 #include <fcntl.h>
 #include <stdlib.h>
 #include <algorithm>
+#include <map>
 #include <string>
 #include <vector>
 #include "update_engine/filesystem_iterator.h"
 #include "update_engine/subprocess.h"
 #include "update_engine/utils.h"
 
+using std::map;
 using std::min;
 using std::string;
 using std::vector;
diff --git a/filesystem_copier_action.h b/filesystem_copier_action.h
index 8f0dc06..3a330ef 100644
--- a/filesystem_copier_action.h
+++ b/filesystem_copier_action.h
@@ -61,7 +61,7 @@
   void TerminateProcessing();
 
   // Used for testing, so we can copy from somewhere other than root
-  void set_copy_source(const string& path) {
+  void set_copy_source(const std::string& path) {
     copy_source_ = path;
   }
   // Returns true if we detected that a copy was unneeded and thus skipped it.
@@ -73,8 +73,8 @@
 
  private:
   // These synchronously mount or unmount the given mountpoint
-  bool Mount(const string& device, const string& mountpoint);
-  bool Unmount(const string& mountpoint);
+  bool Mount(const std::string& device, const std::string& mountpoint);
+  bool Unmount(const std::string& mountpoint);
 
   // Performs a recursive file/directory copy from copy_source_ to dest_path_.
   // Doesn't return until the copy has completed. Returns true on success
@@ -127,11 +127,11 @@
   bool is_mounted_;
 
   // Where the destination device is mounted.
-  string dest_path_;
+  std::string dest_path_;
 
   // The path to copy from. Usually left as the default "/", but tests can
   // change it.
-  string copy_source_;
+  std::string copy_source_;
 
   // The install plan we're passed in via the input pipe.
   InstallPlan install_plan_;
diff --git a/filesystem_copier_action_unittest.cc b/filesystem_copier_action_unittest.cc
index 46c13a3..6d6f0ad 100644
--- a/filesystem_copier_action_unittest.cc
+++ b/filesystem_copier_action_unittest.cc
@@ -4,6 +4,7 @@
 
 #include <glib.h>
 #include <set>
+#include <string>
 #include <vector>
 #include <gtest/gtest.h>
 #include "update_engine/filesystem_copier_action.h"
@@ -13,6 +14,7 @@
 #include "update_engine/utils.h"
 
 using std::set;
+using std::string;
 using std::vector;
 
 namespace chromeos_update_engine {
@@ -32,7 +34,7 @@
 class FilesystemCopierActionTestDelegate : public ActionProcessorDelegate {
  public:
   FilesystemCopierActionTestDelegate() : ran_(false), success_(false) {}
-  void ProcessingDone(const ActionProcessor* processor) {
+  void ProcessingDone(const ActionProcessor* processor, bool success) {
     g_main_loop_quit(loop_);
   }
   void ActionCompleted(ActionProcessor* processor,
diff --git a/gen_coverage_html.sh b/gen_coverage_html.sh
index 1d3d586..39e8c61 100755
--- a/gen_coverage_html.sh
+++ b/gen_coverage_html.sh
@@ -6,7 +6,7 @@
 
 set -ex
 
-scons debug=1 -j 2
+scons debug=1 -j $(cat /proc/cpuinfo |grep '^processor' | wc -l)
 lcov -d . --zerocounters
 ./update_engine_unittests --gtest_filter='-*.RunAsRoot*:*.Fakeroot*'
 fakeroot ./update_engine_unittests --gtest_filter='*.Fakeroot*'
diff --git a/generate_delta_main.cc b/generate_delta_main.cc
index 14af193..b64533b 100644
--- a/generate_delta_main.cc
+++ b/generate_delta_main.cc
@@ -4,45 +4,80 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdio.h>
+#include <errno.h>
 #include <unistd.h>
-
-#include <algorithm>
+#include <set>
 #include <string>
-#include <vector>
-#include <tr1/memory>
-
 #include <glib.h>
-
 #include "chromeos/obsolete_logging.h"
+#include "update_engine/delta_diff_generator.h"
 #include "update_engine/subprocess.h"
 #include "update_engine/update_metadata.pb.h"
-
-using std::sort;
-using std::string;
-using std::vector;
-using std::tr1::shared_ptr;
+#include "update_engine/utils.h"
 
 // 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
 // generates a delta that can be sent to Chrome OS clients.
 
+using std::set;
+using std::string;
+
 namespace chromeos_update_engine {
 
-int main(int argc, char** argv) {
+namespace {
+// These paths should never be delta diffed. They should always be transmitted
+// in full in the update.
+const char* kNonDiffPaths[] = {
+  "/boot/extlinux.conf"
+};
+
+void usage(const char* argv0) {
+  printf("usage: %s old_dir new_dir out_file\n", argv0);
+  exit(1);
+}
+
+bool IsDir(const char* path) {
+  struct stat stbuf;
+  TEST_AND_RETURN_FALSE_ERRNO(lstat(path, &stbuf) == 0);
+  return S_ISDIR(stbuf.st_mode);
+}
+
+int Main(int argc, char** argv) {
   g_thread_init(NULL);
   Subprocess::Init();
   if (argc != 4) {
     usage(argv[0]);
   }
+  logging::InitLogging("",
+                       logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
+                       logging::DONT_LOCK_LOG_FILE,
+                       logging::APPEND_TO_OLD_LOG_FILE);
   const char* old_dir = argv[1];
   const char* new_dir = argv[2];
   if ((!IsDir(old_dir)) || (!IsDir(new_dir))) {
     usage(argv[0]);
   }
-  // TODO(adlr): implement using DeltaDiffGenerator
+  
+  set<string> non_diff_paths;
+  for (size_t i = 0; i < arraysize(kNonDiffPaths); i++)
+    non_diff_paths.insert(kNonDiffPaths[i]);
+  
+  DeltaArchiveManifest* manifest =
+      DeltaDiffGenerator::EncodeMetadataToProtoBuffer(new_dir);
+  CHECK(manifest);
+  CHECK(DeltaDiffGenerator::EncodeDataToDeltaFile(manifest,
+                                                  old_dir,
+                                                  new_dir,
+                                                  argv[3],
+                                                  non_diff_paths,
+                                                  ""));
   return 0;
-}
\ No newline at end of file
+}
+
+}  // namespace {}
+
+}  // namespace chromeos_update_engine
+
+int main(int argc, char** argv) {
+  return chromeos_update_engine::Main(argc, argv);
+}
diff --git a/install_action.cc b/install_action.cc
index c70867e..d144b7b 100644
--- a/install_action.cc
+++ b/install_action.cc
@@ -4,6 +4,7 @@
 
 #include "update_engine/install_action.h"
 #include <errno.h>
+#include <string>
 #include <vector>
 #include <gflags/gflags.h>
 #include "update_engine/filesystem_iterator.h"
@@ -15,6 +16,7 @@
               "If set, the path to use when mounting the "
               "destination device during install");
 
+using std::string;
 using std::vector;
 
 namespace chromeos_update_engine {
@@ -124,12 +126,12 @@
   }
 
   // chmod/chown new file
-  if (!S_ISLNK(file.mode()))
-    TEST_AND_RETURN_FALSE_ERRNO(chmod((mountpoint + path).c_str(), file.mode())
-                                == 0);
   TEST_AND_RETURN_FALSE(file.has_uid() && file.has_gid());
   TEST_AND_RETURN_FALSE_ERRNO(lchown((mountpoint + path).c_str(),
                                      file.uid(), file.gid()) == 0);
+  if (!S_ISLNK(file.mode()))
+    TEST_AND_RETURN_FALSE_ERRNO(chmod((mountpoint + path).c_str(), file.mode())
+                                == 0);
   return true;
 }
 
diff --git a/install_action_unittest.cc b/install_action_unittest.cc
index 68796e6..3fd42d4 100644
--- a/install_action_unittest.cc
+++ b/install_action_unittest.cc
@@ -126,11 +126,16 @@
   }
   const char* const new_dir_cstr = new_dir.c_str();
   EXPECT_EQ(0, System(StringPrintf("mkdir -p '%s/newdir'", new_dir_cstr)));
+  EXPECT_EQ(0, System(StringPrintf("chmod 03755 '%s/newdir'", new_dir_cstr)));
   EXPECT_EQ(0, System(StringPrintf("mkdir -p '%s/newdir/x'", new_dir_cstr)));
   EXPECT_EQ(0, System(StringPrintf("echo -n foo > '%s/newdir/x/file'",
                                    new_dir_cstr)));
+  EXPECT_EQ(0, System(StringPrintf("touch '%s/new_empty'", new_dir_cstr)));
+  EXPECT_EQ(0, System(StringPrintf("chmod 04644 '%s/new_empty'",
+                                   new_dir_cstr)));
   EXPECT_EQ(0, System(StringPrintf("echo -n x >> '%s/big_file'",
                                    new_dir_cstr)));
+  EXPECT_EQ(0, System(StringPrintf("chmod 02644 '%s/big_file'", new_dir_cstr)));
   // Make a symlink that compresses well:
   EXPECT_EQ(0, System(StringPrintf(
       "ln -s "
@@ -150,6 +155,7 @@
                                                         original_dir,
                                                         new_dir,
                                                         "delta",
+                                                        set<string>(),
                                                         new_dir + "/bdev_gz"));
 
   ASSERT_EQ(0, System(string("umount ") + original_dir));
@@ -204,7 +210,7 @@
   }
   LOG(INFO) << "new_count = " << new_count;
   EXPECT_EQ(new_count, original_count);
-  EXPECT_EQ(19, original_count);
+  EXPECT_EQ(20, original_count);
 
   // Make sure hard-link installed properly
   {
diff --git a/integration_unittest.cc b/integration_unittest.cc
index e8eca56..13102da 100644
--- a/integration_unittest.cc
+++ b/integration_unittest.cc
@@ -40,7 +40,7 @@
   virtual ~IntegrationTestProcessorDelegate() {
     EXPECT_TRUE(processing_done_called_);
   }
-  virtual void ProcessingDone(const ActionProcessor* processor) {
+  virtual void ProcessingDone(const ActionProcessor* processor, bool success) {
     processing_done_called_ = true;
     g_main_loop_quit(loop_);
   }
diff --git a/main.cc b/main.cc
index bbb6401..bbd9927 100644
--- a/main.cc
+++ b/main.cc
@@ -2,12 +2,159 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <string>
+#include <tr1/memory>
+#include <vector>
+#include <gflags/gflags.h>
 #include <glib.h>
+#include "chromeos/obsolete_logging.h"
+#include "update_engine/action_processor.h"
+#include "update_engine/download_action.h"
+#include "update_engine/filesystem_copier_action.h"
+#include "update_engine/install_action.h"
+#include "update_engine/libcurl_http_fetcher.h"
+#include "update_engine/omaha_request_prep_action.h"
+#include "update_engine/omaha_response_handler_action.h"
+#include "update_engine/postinstall_runner_action.h"
+#include "update_engine/set_bootable_flag_action.h"
+#include "update_engine/update_check_action.h"
+
+using std::string;
+using std::tr1::shared_ptr;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+class UpdateAttempter : public ActionProcessorDelegate {
+ public:
+  UpdateAttempter(GMainLoop *loop)
+      : full_update_(false),
+        loop_(loop) {}
+  void Update(bool force_full_update);
+  
+  // Delegate method:
+  void ProcessingDone(const ActionProcessor* processor, bool success);
+ private:
+  bool full_update_;
+  vector<shared_ptr<AbstractAction> > actions_;
+  ActionProcessor processor_;
+  GMainLoop *loop_;
+
+  // pointer to the OmahaResponseHandlerAction in the actions_ vector;
+  shared_ptr<OmahaResponseHandlerAction> response_handler_action_;
+  DISALLOW_COPY_AND_ASSIGN(UpdateAttempter);
+};
+
+// 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 UpdateAttempter::Update(bool force_full_update) {
+  full_update_ = force_full_update;
+  CHECK(!processor_.IsRunning());
+  processor_.set_delegate(this);
+
+  // Actions:
+  shared_ptr<OmahaRequestPrepAction> request_prep_action(
+      new OmahaRequestPrepAction(force_full_update));
+  shared_ptr<UpdateCheckAction> update_check_action(
+      new UpdateCheckAction(new LibcurlHttpFetcher));
+  shared_ptr<OmahaResponseHandlerAction> response_handler_action(
+      new OmahaResponseHandlerAction);
+  shared_ptr<FilesystemCopierAction> filesystem_copier_action(
+      new FilesystemCopierAction);
+  shared_ptr<DownloadAction> download_action(
+      new DownloadAction(new LibcurlHttpFetcher));
+  shared_ptr<InstallAction> install_action(
+      new InstallAction);
+  shared_ptr<PostinstallRunnerAction> postinstall_runner_action(
+      new PostinstallRunnerAction);
+  shared_ptr<SetBootableFlagAction> set_bootable_flag_action(
+      new SetBootableFlagAction);
+      
+  response_handler_action_ = response_handler_action;
+
+  actions_.push_back(shared_ptr<AbstractAction>(request_prep_action));
+  actions_.push_back(shared_ptr<AbstractAction>(update_check_action));
+  actions_.push_back(shared_ptr<AbstractAction>(response_handler_action));
+  actions_.push_back(shared_ptr<AbstractAction>(filesystem_copier_action));
+  actions_.push_back(shared_ptr<AbstractAction>(download_action));
+  actions_.push_back(shared_ptr<AbstractAction>(install_action));
+  actions_.push_back(shared_ptr<AbstractAction>(postinstall_runner_action));
+  actions_.push_back(shared_ptr<AbstractAction>(set_bootable_flag_action));
+  
+  // 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(request_prep_action.get(), update_check_action.get());
+  BondActions(update_check_action.get(), response_handler_action.get());
+  BondActions(response_handler_action.get(), filesystem_copier_action.get());
+  BondActions(filesystem_copier_action.get(), download_action.get());
+  BondActions(download_action.get(), install_action.get());
+  BondActions(install_action.get(), postinstall_runner_action.get());
+  BondActions(postinstall_runner_action.get(), set_bootable_flag_action.get());
+
+  processor_.StartProcessing();
+}
+
+void UpdateAttempter::ProcessingDone(const ActionProcessor* processor,
+                                     bool success) {
+  CHECK(response_handler_action_);
+  if (response_handler_action_->GotNoUpdateResponse()) {
+    // All done.
+    g_main_loop_quit(loop_);
+    return;
+  }
+  if (!success) {
+    if (!full_update_) {
+      LOG(ERROR) << "Update failed. Attempting full update";
+      actions_.clear();
+      response_handler_action_.reset();
+      Update(true);
+      return;
+    } else {
+      LOG(ERROR) << "Full update failed. Aborting";
+    }
+  }
+  g_main_loop_quit(loop_);
+}
+
+gboolean UpdateInMainLoop(void* arg) {
+  UpdateAttempter* update_attempter = reinterpret_cast<UpdateAttempter*>(arg);
+  update_attempter->Update(false);
+  return FALSE;  // Don't call this callback function again
+}
+
+}  // namespace chromeos_update_engine
 
 #include "update_engine/subprocess.h"
 
 int main(int argc, char** argv) {
   g_thread_init(NULL);
   chromeos_update_engine::Subprocess::Init();
+  google::ParseCommandLineFlags(&argc, &argv, true);
+  // TODO(adlr): figure out log file
+  logging::InitLogging("",
+                       logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
+                       logging::DONT_LOCK_LOG_FILE,
+                       logging::APPEND_TO_OLD_LOG_FILE);
+  LOG(INFO) << "Chrome OS Update Engine starting";
+  
+  // Create the single GMainLoop
+  GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
+
+  chromeos_update_engine::UpdateAttempter update_attempter(loop);
+
+  g_timeout_add(0, &chromeos_update_engine::UpdateInMainLoop,
+                &update_attempter);
+
+  g_main_loop_run(loop);
+  g_main_loop_unref(loop);
+
+  LOG(INFO) << "Chrome OS Update Engine terminating";
   return 0;
 }
diff --git a/omaha_request_prep_action.cc b/omaha_request_prep_action.cc
index 8c015d8..9e144a0 100644
--- a/omaha_request_prep_action.cc
+++ b/omaha_request_prep_action.cc
@@ -16,7 +16,8 @@
 
 namespace {
 const string OmahaIdPath() {
-  return chromeos_update_engine::utils::kStatefulPartition + "/etc/omaha_id";
+  return string(chromeos_update_engine::utils::kStatefulPartition) +
+      "/etc/omaha_id";
 }
 }  // namespace {}
 
@@ -89,7 +90,7 @@
 }
 
 std::string OmahaRequestPrepAction::GetLsbValue(const std::string& key) const {
-  string files[] = {utils::kStatefulPartition + "/etc/lsb-release",
+  string files[] = {string(utils::kStatefulPartition) + "/etc/lsb-release",
                     "/etc/lsb-release"};
   for (unsigned int i = 0; i < arraysize(files); i++) {
     string file_data;
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
index 0a8472e..0a0a660 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -22,6 +22,7 @@
   ScopedActionCompleter completer(processor_, this);
   const UpdateCheckResponse& response = GetInputObject();
   if (!response.update_exists) {
+    got_no_update_response_ = true;
     LOG(INFO) << "There are no updates. Aborting.";
     return;
   }
@@ -47,7 +48,8 @@
     filename.resize(255);
   }
   // TODO(adlr): come up with a better place to download to:
-  install_plan.download_path = utils::kStatefulPartition + "/" + filename;
+  install_plan.download_path = string(utils::kStatefulPartition) + "/" +
+      filename;
   if (HasOutputPipe())
     SetOutputObject(install_plan);
   LOG(INFO) << "Using this install plan:";
diff --git a/omaha_response_handler_action.h b/omaha_response_handler_action.h
index e25de28..b56d343 100644
--- a/omaha_response_handler_action.h
+++ b/omaha_response_handler_action.h
@@ -26,7 +26,7 @@
 
 class OmahaResponseHandlerAction : public Action<OmahaResponseHandlerAction> {
  public:
-  OmahaResponseHandlerAction() {}
+  OmahaResponseHandlerAction() : got_no_update_response_(false) {}
   typedef ActionTraits<OmahaResponseHandlerAction>::InputObjectType
       InputObjectType;
   typedef ActionTraits<OmahaResponseHandlerAction>::OutputObjectType
@@ -41,6 +41,8 @@
   void set_boot_device(const std::string& boot_device) {
     boot_device_ = boot_device;
   }
+  
+  bool GotNoUpdateResponse() const { return got_no_update_response_; }
 
   // Debugging/logging
   static std::string StaticType() { return "OmahaResponseHandlerAction"; }
@@ -56,6 +58,9 @@
 
   // set to non-empty in unit tests
   std::string boot_device_;
+  
+  // True only if we got a response and the response said no updates
+  bool got_no_update_response_;
 
   DISALLOW_COPY_AND_ASSIGN(OmahaResponseHandlerAction);
 };
diff --git a/omaha_response_handler_action_unittest.cc b/omaha_response_handler_action_unittest.cc
index 0248daf..a76838a 100644
--- a/omaha_response_handler_action_unittest.cc
+++ b/omaha_response_handler_action_unittest.cc
@@ -93,7 +93,8 @@
     EXPECT_TRUE(install_plan.is_full_update);
     EXPECT_EQ(in.codebase, install_plan.download_url);
     EXPECT_EQ(in.hash, install_plan.download_hash);
-    EXPECT_EQ(utils::kStatefulPartition + "/the_update_a.b.c.d_FULL_.tgz",
+    EXPECT_EQ(string(utils::kStatefulPartition) +
+              "/the_update_a.b.c.d_FULL_.tgz",
               install_plan.download_path);
     EXPECT_EQ("/dev/sda2", install_plan.install_path);
   }
@@ -112,7 +113,8 @@
     EXPECT_FALSE(install_plan.is_full_update);
     EXPECT_EQ(in.codebase, install_plan.download_url);
     EXPECT_EQ(in.hash, install_plan.download_hash);
-    EXPECT_EQ(utils::kStatefulPartition + "/the_update_a.b.c.d_DELTA_.tgz",
+    EXPECT_EQ(string(utils::kStatefulPartition) +
+              "/the_update_a.b.c.d_DELTA_.tgz",
               install_plan.download_path);
     EXPECT_EQ("/dev/sda3", install_plan.install_path);
   }
@@ -131,7 +133,8 @@
     EXPECT_FALSE(install_plan.is_full_update);
     EXPECT_EQ(in.codebase, install_plan.download_url);
     EXPECT_EQ(in.hash, install_plan.download_hash);
-    EXPECT_EQ(utils::kStatefulPartition + "/" + kLongName.substr(0, 255),
+    EXPECT_EQ(string(utils::kStatefulPartition) + "/" +
+              kLongName.substr(0, 255),
               install_plan.download_path);
     EXPECT_EQ("/dev/sda3", install_plan.install_path);
   }
diff --git a/postinstall_runner_action.cc b/postinstall_runner_action.cc
index 4122860..98de1bd 100644
--- a/postinstall_runner_action.cc
+++ b/postinstall_runner_action.cc
@@ -12,7 +12,7 @@
 using std::string;
 
 namespace {
-const string kMountPath(utils::kStatefulPartition + "/au_destination");
+const string kMountPath(string(utils::kStatefulPartition) + "/au_destination");
 const string kPostinstallScript("/postinst");
 }
 
diff --git a/postinstall_runner_action_unittest.cc b/postinstall_runner_action_unittest.cc
index c43cb8a..5bb515f 100644
--- a/postinstall_runner_action_unittest.cc
+++ b/postinstall_runner_action_unittest.cc
@@ -56,7 +56,8 @@
   ASSERT_EQ(0, getuid()) << "Run me as root. Ideally don't run other tests "
                          << "as root, tho.";
 
-  const string mountpoint(utils::kStatefulPartition + "/au_destination");
+  const string mountpoint(string(utils::kStatefulPartition) +
+                          "/au_destination");
 
   string cwd;
   {
diff --git a/test_utils.cc b/test_utils.cc
index d916458..f83ddf9 100644
--- a/test_utils.cc
+++ b/test_utils.cc
@@ -177,26 +177,25 @@
 
 void CreateExtImageAtPath(const string& path, vector<string>* out_paths) {
   // create 10MiB sparse file
-  const char* const mount_path = kMountPath.c_str();
   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("mkdir -p %s", mount_path)));
+  EXPECT_EQ(0, System(StringPrintf("mkdir -p %s", kMountPath)));
   EXPECT_EQ(0, System(StringPrintf("mount -o loop %s %s", path.c_str(),
-                                   mount_path)));
-  EXPECT_EQ(0, System(StringPrintf("echo hi > %s/hi", mount_path)));
-  EXPECT_EQ(0, System(StringPrintf("echo hello > %s/hello", mount_path)));
-  EXPECT_EQ(0, System(StringPrintf("mkdir %s/some_dir", mount_path)));
-  EXPECT_EQ(0, System(StringPrintf("mkdir %s/some_dir/empty_dir", mount_path)));
-  EXPECT_EQ(0, System(StringPrintf("mkdir %s/some_dir/mnt", mount_path)));
-  EXPECT_EQ(0, System(StringPrintf("echo T > %s/some_dir/test", mount_path)));
-  EXPECT_EQ(0, System(StringPrintf("mkfifo %s/some_dir/fifo", mount_path)));
-  EXPECT_EQ(0, System(StringPrintf("mknod %s/cdev c 2 3", mount_path)));
-  EXPECT_EQ(0, System(StringPrintf("ln -s /some/target %s/sym", mount_path)));
+                                   kMountPath)));
+  EXPECT_EQ(0, System(StringPrintf("echo hi > %s/hi", kMountPath)));
+  EXPECT_EQ(0, System(StringPrintf("echo hello > %s/hello", kMountPath)));
+  EXPECT_EQ(0, System(StringPrintf("mkdir %s/some_dir", kMountPath)));
+  EXPECT_EQ(0, System(StringPrintf("mkdir %s/some_dir/empty_dir", kMountPath)));
+  EXPECT_EQ(0, System(StringPrintf("mkdir %s/some_dir/mnt", kMountPath)));
+  EXPECT_EQ(0, System(StringPrintf("echo T > %s/some_dir/test", kMountPath)));
+  EXPECT_EQ(0, System(StringPrintf("mkfifo %s/some_dir/fifo", kMountPath)));
+  EXPECT_EQ(0, System(StringPrintf("mknod %s/cdev c 2 3", kMountPath)));
+  EXPECT_EQ(0, System(StringPrintf("ln -s /some/target %s/sym", kMountPath)));
   EXPECT_EQ(0, System(StringPrintf("ln %s/some_dir/test %s/testlink",
-                                   mount_path, mount_path)));
-  EXPECT_EQ(0, System(StringPrintf("umount %s", mount_path)));
+                                   kMountPath, kMountPath)));
+  EXPECT_EQ(0, System(StringPrintf("umount %s", kMountPath)));
   
   if (out_paths) {
     out_paths->clear();
diff --git a/test_utils.h b/test_utils.h
index 6274a22..cd17ec1 100644
--- a/test_utils.h
+++ b/test_utils.h
@@ -88,7 +88,7 @@
   0xbe, 0x9f, 0xa3, 0x5d
 };
 
-const string kMountPath("/tmp/UpdateEngineTests_mnt");
+const char* const kMountPath = "/tmp/UpdateEngineTests_mnt";
 }  // namespace {}
 
 // Creates an ext image with some files in it. The paths creates are
diff --git a/update_check_action.h b/update_check_action.h
index 402943b..5622a63 100644
--- a/update_check_action.h
+++ b/update_check_action.h
@@ -20,8 +20,6 @@
 // The Update Check action makes an update check request to Omaha and
 // can output the response on the output ActionPipe.
 
-using std::string;
-
 namespace chromeos_update_engine {
 
 // Encodes XML entities in a given string with libxml2. input must be
diff --git a/update_check_action_unittest.cc b/update_check_action_unittest.cc
index 723fc22..417a47e 100644
--- a/update_check_action_unittest.cc
+++ b/update_check_action_unittest.cc
@@ -54,7 +54,7 @@
         expected_success_(true) {}
   virtual ~UpdateCheckActionTestProcessorDelegate() {
   }
-  virtual void ProcessingDone(const ActionProcessor* processor) {
+  virtual void ProcessingDone(const ActionProcessor* processor, bool success) {
     ASSERT_TRUE(loop_);
     g_main_loop_quit(loop_);
   }
diff --git a/update_metadata.proto b/update_metadata.proto
index 61b4965..42880da 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -102,6 +102,8 @@
     }
     // If present, there is data associated with this File object and
     // data_offset and data_size must be set.
+    // If a file object doesn't have this set, it means the data is
+    // unchanged from the old version of the file.
     optional DataFormat data_format = 4;
     // The offset into the delta file where the data (if any) is stored
     optional uint32 data_offset = 5;
diff --git a/utils.cc b/utils.cc
index 9fa0906..69a59cc 100644
--- a/utils.cc
+++ b/utils.cc
@@ -257,7 +257,7 @@
   return true;
 }
 
-const string kStatefulPartition = "/mnt/stateful_partition";
+const char* const kStatefulPartition = "/mnt/stateful_partition";
 
 }  // namespace utils
 
diff --git a/utils.h b/utils.h
index 7920f3c..de998f4 100644
--- a/utils.h
+++ b/utils.h
@@ -38,7 +38,7 @@
 // of the string passed in.
 // NEVER CALL THIS FUNCTION UNLESS YOU ARE SURE
 // THAT YOUR PROCESS WILL BE THE ONLY THING WRITING FILES IN THIS DIRECTORY.
-std::string TempFilename(string path);
+std::string TempFilename(std::string path);
 
 // 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.
@@ -47,8 +47,8 @@
 
 // Synchronously mount or unmount a filesystem. Return true on success.
 // Mounts as ext3 with default options.
-bool MountFilesystem(const string& device, const string& mountpoint);
-bool UnmountFilesystem(const string& mountpoint);
+bool MountFilesystem(const std::string& device, const std::string& mountpoint);
+bool UnmountFilesystem(const std::string& mountpoint);
 
 // Log a string in hex to LOG(INFO). Useful for debugging.
 void HexDumpArray(const unsigned char* const arr, const size_t length);
@@ -59,7 +59,7 @@
   HexDumpArray(reinterpret_cast<const unsigned char*>(&vect[0]), vect.size());
 }
 
-extern const string kStatefulPartition;
+extern const char* const kStatefulPartition;
 
 bool StringHasSuffix(const std::string& str, const std::string& suffix);
 bool StringHasPrefix(const std::string& str, const std::string& prefix);
@@ -68,6 +68,10 @@
 bool MapContainsKey(const std::map<KeyType, ValueType>& m, const KeyType& k) {
   return m.find(k) != m.end();
 }
+template<typename KeyType>
+bool SetContainsKey(const std::set<KeyType>& s, const KeyType& k) {
+  return s.find(k) != s.end();
+}
 
 template<typename ValueType>
 std::set<ValueType> SetWithValue(const ValueType& value) {
diff --git a/utils_unittest.cc b/utils_unittest.cc
index 4fee544..74f349d 100644
--- a/utils_unittest.cc
+++ b/utils_unittest.cc
@@ -5,10 +5,12 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <errno.h>
+#include <string>
 #include <vector>
 #include <gtest/gtest.h>
 #include "update_engine/utils.h"
 
+using std::string;
 using std::vector;
 
 namespace chromeos_update_engine {