AU: SplitWriter class for parsing our full update files.

Full updates now include data for two partitions (kernel + rootfs).
This CL adds a new SplitFileWriter class which takes the stream of
data, which is expected to be in our full update format (8 bytes big
endian uint64_t of the first partition size, followed by first
partition data, followed by second partition size). It parses the
size, then writes the data to each of two FileWriter classes.

BUG=None
TEST=Attached unittest

Review URL: http://codereview.chromium.org/1800009
diff --git a/SConstruct b/SConstruct
index 6855425..dc05e37 100644
--- a/SConstruct
+++ b/SConstruct
@@ -170,6 +170,7 @@
                    omaha_response_handler_action.cc
                    postinstall_runner_action.cc
                    set_bootable_flag_action.cc
+                   split_file_writer.cc
                    subprocess.cc
                    tarjan.cc
                    topological_sort.cc
@@ -201,6 +202,7 @@
                             omaha_response_handler_action_unittest.cc
                             postinstall_runner_action_unittest.cc
                             set_bootable_flag_action_unittest.cc
+                            split_file_writer_unittest.cc
                             subprocess_unittest.cc
                             tarjan_unittest.cc
                             test_utils.cc
diff --git a/decompressing_file_writer.cc b/decompressing_file_writer.cc
old mode 100644
new mode 100755
index 96db010..0a3280d
--- a/decompressing_file_writer.cc
+++ b/decompressing_file_writer.cc
@@ -27,7 +27,7 @@
 //      uLong   reserved;   /* reserved for future use */
 //  } z_stream;
 
-int GzipDecompressingFileWriter::Write(const void* bytes, size_t count) {
+ssize_t GzipDecompressingFileWriter::Write(const void* bytes, size_t count) {
   // Steps:
   // put the data on next_in
   // call inflate until it returns nothing, each time writing what we get
diff --git a/decompressing_file_writer.h b/decompressing_file_writer.h
index 6f12e87..cd8e294 100644
--- a/decompressing_file_writer.h
+++ b/decompressing_file_writer.h
@@ -36,7 +36,7 @@
   // stream, after the entire stream has been written to this object,
   // the entire decompressed stream will have been written to the
   // underlying FileWriter.
-  virtual int Write(const void* bytes, size_t count);
+  virtual ssize_t Write(const void* bytes, size_t count);
 
   virtual int Close() {
     return next_->Close();
diff --git a/delta_performer.cc b/delta_performer.cc
index 79aea61..765fc1e 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -110,7 +110,7 @@
 // -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) {
+ssize_t 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);
 
diff --git a/delta_performer.h b/delta_performer.h
index 1356bae..4a096c2 100644
--- a/delta_performer.h
+++ b/delta_performer.h
@@ -30,7 +30,7 @@
 
   // Wrapper around write. Returns bytes written on success or
   // -errno on error.
-  int Write(const void* bytes, size_t count);
+  ssize_t Write(const void* bytes, size_t count);
 
   // Wrapper around close. Returns 0 on success or -errno on error.
   int Close();
diff --git a/file_writer.cc b/file_writer.cc
index 07bdb46..1a95a75 100644
--- a/file_writer.cc
+++ b/file_writer.cc
@@ -15,7 +15,7 @@
   return 0;
 }
 
-int DirectFileWriter::Write(const void* bytes, size_t count) {
+ssize_t DirectFileWriter::Write(const void* bytes, size_t count) {
   CHECK_GE(fd_, 0);
   const char* char_bytes = reinterpret_cast<const char*>(bytes);
 
diff --git a/file_writer.h b/file_writer.h
index 37207d0..ebb8535 100644
--- a/file_writer.h
+++ b/file_writer.h
@@ -29,7 +29,7 @@
 
   // Wrapper around write. Returns bytes written on success or
   // -errno on error.
-  virtual int Write(const void* bytes, size_t count) = 0;
+  virtual ssize_t Write(const void* bytes, size_t count) = 0;
 
   // Wrapper around close. Returns 0 on success or -errno on error.
   virtual int Close() = 0;
@@ -47,7 +47,7 @@
   virtual ~DirectFileWriter() {}
 
   virtual int Open(const char* path, int flags, mode_t mode);
-  virtual int Write(const void* bytes, size_t count);
+  virtual ssize_t Write(const void* bytes, size_t count);
   virtual int Close();
 
   int fd() const { return fd_; }
diff --git a/mock_file_writer.h b/mock_file_writer.h
index 7cf0fd5..f2785cc 100644
--- a/mock_file_writer.h
+++ b/mock_file_writer.h
@@ -25,7 +25,7 @@
     return 0;
   }
 
-  virtual int Write(const void* bytes, size_t count) {
+  virtual ssize_t Write(const void* bytes, size_t count) {
     CHECK(was_opened_);
     CHECK(!was_closed_);
     const char* char_bytes = reinterpret_cast<const char*>(bytes);
diff --git a/split_file_writer.cc b/split_file_writer.cc
new file mode 100644
index 0000000..e868947
--- /dev/null
+++ b/split_file_writer.cc
@@ -0,0 +1,113 @@
+// 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/split_file_writer.h"
+#include <algorithm>
+
+using std::min;
+
+namespace chromeos_update_engine {
+
+int SplitFileWriter::Open(const char* path, int flags, mode_t mode) {
+  int first_result = first_file_writer_->Open(first_path_,
+  first_flags_,
+  first_mode_);
+  if (first_result < 0) {
+    LOG(ERROR) << "Error opening first file " << first_path_;
+    return first_result;
+  }
+  int second_result = second_file_writer_->Open(path, flags, mode);
+  if (second_result < 0) {
+    LOG(ERROR) << "Error opening second file " << path;
+    first_file_writer_->Close();
+    return second_result;
+  }
+  return second_result;
+}
+
+namespace {
+ssize_t PerformWrite(FileWriter* writer, const void* bytes, size_t count) {
+  int rc = writer->Write(bytes, count);
+  if (rc < 0) {
+    LOG(ERROR) << "Write failed to file.";
+    return rc;
+  }
+  if (rc != static_cast<int>(count)) {
+    LOG(ERROR) << "Not all bytes successfully written to file.";
+    return -EIO;
+  }
+  return rc;
+}
+}
+
+ssize_t SplitFileWriter::Write(const void* bytes, size_t count) {
+  const size_t original_count = count;
+  
+  // This first block is trying to read the first sizeof(uint64_t)
+  // bytes, which are the number of bytes that should be written
+  // to the first FileWriter.
+  if (bytes_received_ < static_cast<off_t>(sizeof(uint64_t))) {
+    // Write more to the initial buffer
+    size_t bytes_to_copy = min(count,
+                               sizeof(first_length_buf_) - bytes_received_);
+    memcpy(&first_length_buf_[bytes_received_], bytes, bytes_to_copy);
+    bytes_received_ += bytes_to_copy;
+    count -= bytes_to_copy;
+    bytes = static_cast<const void*>(
+        static_cast<const char*>(bytes) + bytes_to_copy);
+
+    // See if we have all we need
+    if (bytes_received_ == sizeof(first_length_buf_)) {
+      // Parse first number
+      uint64_t big_endian_first_length;
+      memcpy(&big_endian_first_length, first_length_buf_,
+             sizeof(big_endian_first_length));
+      first_length_ = be64toh(big_endian_first_length);
+    }
+    if (count == 0)
+      return original_count;
+  }
+  CHECK_GE(bytes_received_, static_cast<off_t>(sizeof(uint64_t)));
+
+  // This block of code is writing to the first FileWriter.
+  if (bytes_received_ - static_cast<off_t>(sizeof(uint64_t)) < first_length_) {
+    // Write to first FileWriter
+    size_t bytes_to_write = min(
+        first_length_ -
+        (bytes_received_ - static_cast<off_t>(sizeof(uint64_t))),
+        static_cast<off_t>(count));
+      
+    int rc = PerformWrite(first_file_writer_, bytes, bytes_to_write);
+    if (rc != static_cast<int>(bytes_to_write))
+      return rc;
+    
+    bytes_received_ += bytes_to_write;
+    count -= bytes_to_write;
+    bytes = static_cast<const void*>(
+        static_cast<const char*>(bytes) + bytes_to_write);
+    if (count == 0)
+      return original_count;
+  }
+
+  CHECK_GE(static_cast<off_t>(bytes_received_),
+           first_length_ + static_cast<off_t>(sizeof(uint64_t)));
+  // Write to second FileWriter
+  int rc = PerformWrite(second_file_writer_, bytes, count);
+  if (rc != static_cast<int>(count))
+    return rc;
+  return original_count;
+}
+
+int SplitFileWriter::Close() {
+  int first_result = first_file_writer_->Close();
+  if (first_result < 0)
+    LOG(ERROR) << "Error Close()ing first file.";
+  int second_result = second_file_writer_->Close();
+  if (second_result < 0)
+    LOG(ERROR) << "Error Close()ing second file.";
+  // Return error if either had returned error.
+  return second_result < 0 ? second_result : first_result;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/split_file_writer.h b/split_file_writer.h
new file mode 100644
index 0000000..cba8161
--- /dev/null
+++ b/split_file_writer.h
@@ -0,0 +1,63 @@
+// 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_SPLIT_FILE_WRITER_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_SPLIT_FILE_WRITER_H__
+
+#include "update_engine/file_writer.h"
+
+// SplitFileWriter is an implementation of FileWriter suited to our
+// full autoupdate format. The first 8 bytes read are assumed to be a
+// big-endian number describing how many of the next following bytes
+// go to the first FileWriter. After that, the rest of the bytes all
+// go to the second FileWriter.
+
+namespace chromeos_update_engine {
+
+class SplitFileWriter : public FileWriter {
+ public:
+  SplitFileWriter(FileWriter* first_file_writer, FileWriter* second_file_writer)
+      : first_file_writer_(first_file_writer),
+        first_length_(0),
+        first_path_(NULL),
+        first_flags_(0),
+        first_mode_(0),
+        second_file_writer_(second_file_writer),
+        bytes_received_(0) {}
+  
+  void SetFirstOpenArgs(const char* path, int flags, mode_t mode) {
+    first_path_ = path;
+    first_flags_ = flags;
+    first_mode_ = mode;
+  }
+  
+  // If both succeed, returns the return value from the second Open() call.
+  // On error, both files will be left closed.
+  virtual int Open(const char* path, int flags, mode_t mode);
+
+  virtual ssize_t Write(const void* bytes, size_t count);
+
+  virtual int Close();
+
+ private:
+  // Data for the first file writer.
+  FileWriter* const first_file_writer_;
+  off_t first_length_;
+  const char* first_path_;
+  int first_flags_;
+  mode_t first_mode_;
+  
+  // The scond file writeer.
+  FileWriter* const second_file_writer_;
+
+  // Bytes written thus far
+  off_t bytes_received_;
+  char first_length_buf_[sizeof(uint64_t)];
+
+  DISALLOW_COPY_AND_ASSIGN(SplitFileWriter);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_SPLIT_FILE_WRITER_H__
diff --git a/split_file_writer_unittest.cc b/split_file_writer_unittest.cc
new file mode 100644
index 0000000..5720e27
--- /dev/null
+++ b/split_file_writer_unittest.cc
@@ -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.
+
+#include <algorithm>
+#include <string>
+#include <vector>
+#include <gtest/gtest.h>
+#include "update_engine/split_file_writer.h"
+#include "update_engine/test_utils.h"
+#include "update_engine/utils.h"
+
+using std::min;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+class SplitFileWriterTest : public ::testing::Test {
+ protected:
+  void DoTest(size_t bytes_per_call);
+};
+
+void SplitFileWriterTest::DoTest(size_t bytes_per_call) {
+  string first_file;
+  EXPECT_TRUE(utils::MakeTempFile("/tmp/SplitFileWriterTestFirst.XXXXXX",
+                                  &first_file,
+                                  NULL));
+  ScopedPathUnlinker first_file_unlinker(first_file);
+  string second_file;
+  EXPECT_TRUE(utils::MakeTempFile("/tmp/SplitFileWriterTestSecond.XXXXXX",
+                                  &second_file,
+                                  NULL));
+  ScopedPathUnlinker second_file_unlinker(second_file);
+
+  // Create joined data
+  uint64_t first_bytes = 789;
+  uint64_t second_bytes = 1000;
+
+  vector<char> buf(sizeof(uint64_t) + first_bytes + second_bytes);
+  uint64_t big_endian_first_bytes = htobe64(first_bytes);
+
+  FillWithData(&buf);
+  memcpy(&buf[0], &big_endian_first_bytes, sizeof(big_endian_first_bytes));
+  
+  // Create FileWriters
+  DirectFileWriter first_file_writer;
+  DirectFileWriter second_file_writer;
+  SplitFileWriter split_file_writer(&first_file_writer, &second_file_writer);
+
+  split_file_writer.SetFirstOpenArgs(
+      first_file.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644);
+  EXPECT_GE(split_file_writer.Open(
+      second_file.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644), 0);
+  ScopedFileWriterCloser closer(&split_file_writer);
+
+  size_t bytes_written_total = 0;
+  for (size_t i = 0; i < buf.size(); i += bytes_per_call) {
+    size_t bytes_this_iteration = min(bytes_per_call,
+                                      buf.size() - bytes_written_total);
+    EXPECT_EQ(bytes_this_iteration,
+              split_file_writer.Write(&buf[bytes_written_total],
+                                      bytes_this_iteration));
+    bytes_written_total += bytes_this_iteration;
+  }
+  EXPECT_EQ(buf.size(), bytes_written_total);
+
+  vector<char> first_actual_data;
+  vector<char> second_actual_data;
+  EXPECT_TRUE(utils::ReadFile(first_file, &first_actual_data));
+  EXPECT_TRUE(utils::ReadFile(second_file, &second_actual_data));
+  
+  EXPECT_EQ(first_bytes, first_actual_data.size());
+  EXPECT_EQ(second_bytes, second_actual_data.size());
+
+  vector<char> first_part_from_orig(&buf[sizeof(uint64_t)],
+                                    &buf[sizeof(uint64_t) + first_bytes]);
+  vector<char> second_part_from_orig(
+      &buf[sizeof(uint64_t) + first_bytes],
+      &buf[sizeof(uint64_t) + first_bytes + second_bytes]);
+
+  EXPECT_TRUE(ExpectVectorsEq(first_part_from_orig, first_actual_data));
+  EXPECT_TRUE(ExpectVectorsEq(second_part_from_orig, second_actual_data));
+}
+
+TEST_F(SplitFileWriterTest, SimpleTest) {
+  DoTest(1024 * 1024 * 1024);  // Something very big
+}
+
+TEST_F(SplitFileWriterTest, OneByteAtATimeTest) {
+  DoTest(1);
+}
+
+TEST_F(SplitFileWriterTest, ThreeBytesAtATimeTest) {
+  DoTest(3);
+}
+
+}  // namespace chromeos_update_engine