Missed new files in last commit

Review URL: http://codereview.chromium.org/465067


git-svn-id: svn://chrome-svn/chromeos/trunk@336 06c00378-0e64-4dae-be16-12b19f9950a1
diff --git a/delta_diff_parser.cc b/delta_diff_parser.cc
new file mode 100644
index 0000000..47c763f
--- /dev/null
+++ b/delta_diff_parser.cc
@@ -0,0 +1,272 @@
+// 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"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <algorithm>
+#include <string>
+#include <vector>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include "base/scoped_ptr.h"
+#include "update_engine/decompressing_file_writer.h"
+#include "update_engine/gzip.h"
+#include "update_engine/utils.h"
+
+using std::min;
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+const int kCopyFileBufferSize = 4096;
+}
+
+const char* const DeltaDiffParser::kFileMagic("CrAU");
+
+// The iterator returns a directory before returning its children.
+// Steps taken in Increment():
+//   - See if the current item has children. If so, the child becomes
+//     the new current item and we return.
+//   - If current item has no children, we loop. Each loop iteration
+//     considers an item (first the current item, then its parent,
+//     then grand parent, and so on). Each loop iteration, we see if there
+//     are any siblings we haven't iterated on yet. If so, we're done.
+//     If not, keep looping to parents.
+void DeltaDiffParserIterator::Increment() {
+  // See if we have any children.
+  const DeltaArchiveManifest_File& file = GetFile();
+  if (file.children_size() > 0) {
+    path_indices_.push_back(file.children(0).index());
+    path_ += "/";
+    path_ += file.children(0).name();
+    child_indices_.push_back(0);
+    return;
+  }
+  // Look in my parent for the next child, then try grandparent, etc.
+
+  path_indices_.pop_back();
+  path_.resize(path_.rfind('/'));
+
+  while (!child_indices_.empty()) {
+    // Try to bump the last entry
+    CHECK_EQ(path_indices_.size(), child_indices_.size());
+    child_indices_.back()++;
+    const DeltaArchiveManifest_File& parent =
+        archive_->files(path_indices_.back());
+    if (parent.children_size() > child_indices_.back()) {
+      // we found a new child!
+      path_indices_.push_back(parent.children(child_indices_.back()).index());
+      path_ += "/";
+      path_ += parent.children(child_indices_.back()).name();
+      return;
+    }
+    path_indices_.pop_back();
+    child_indices_.pop_back();
+    if (!path_.empty())
+      path_.resize(path_.rfind('/'));
+  }
+}
+
+const string DeltaDiffParserIterator::GetName() const {
+  if (path_.empty())
+    return "";
+  CHECK_NE(path_.rfind('/'), string::npos);
+  return string(path_, path_.rfind('/') + 1);
+}
+
+const DeltaArchiveManifest_File& DeltaDiffParserIterator::GetFile() const {
+  CHECK(!path_indices_.empty());
+  return archive_->files(path_indices_.back());
+}
+
+
+DeltaDiffParser::DeltaDiffParser(const string& delta_file)
+    : fd_(-1),
+      valid_(false) {
+  fd_ = open(delta_file.c_str(), O_RDONLY, 0);
+  if (fd_ < 0) {
+    LOG(ERROR) << "Unable to open delta file: " << delta_file;
+    return;
+  }
+  ScopedFdCloser fd_closer(&fd_);
+  scoped_array<char> magic(new char[strlen(kFileMagic)]);
+  if (strlen(kFileMagic) != read(fd_, magic.get(), strlen(kFileMagic))) {
+    LOG(ERROR) << "delta file too short";
+    return;
+  }
+  if (strncmp(magic.get(), kFileMagic, strlen(kFileMagic))) {
+    LOG(ERROR) << "Incorrect magic at beginning of delta file";
+    return;
+  }
+
+  int64 proto_offset = 0;
+  COMPILE_ASSERT(sizeof(proto_offset) == sizeof(off_t), off_t_wrong_size);
+  if (sizeof(proto_offset) != read(fd_, &proto_offset, sizeof(proto_offset))) {
+    LOG(ERROR) << "delta file too short";
+    return;
+  }
+  proto_offset = be64toh(proto_offset);  // switch from big-endian to host
+
+  int64 proto_length = 0;
+  if (sizeof(proto_length) != read(fd_, &proto_length, sizeof(proto_length))) {
+    LOG(ERROR) << "delta file too short";
+    return;
+  }
+  proto_length = be64toh(proto_length);  // switch from big-endian to host
+
+  vector<char> proto(proto_length);
+  size_t bytes_read = 0;
+  while (bytes_read < proto_length) {
+    ssize_t r = pread(fd_, &proto[bytes_read], proto_length - bytes_read,
+                      proto_offset + bytes_read);
+    TEST_AND_RETURN(r >= 0);
+    bytes_read += r;
+  }
+  {
+    vector<char> decompressed_proto;
+    TEST_AND_RETURN(GzipDecompress(proto, &decompressed_proto));
+    proto.swap(decompressed_proto);
+  }
+
+  valid_ = archive_.ParseFromArray(&proto[0], proto.size());
+  if (valid_) {
+    fd_closer.set_should_close(false);
+  } else {
+    LOG(ERROR) << "load from file failed";
+  }
+}
+
+DeltaDiffParser::~DeltaDiffParser() {
+  if (fd_ >= 0) {
+    close(fd_);
+    fd_ = -1;
+  }
+}
+
+bool DeltaDiffParser::ContainsPath(const string& path) const {
+  return GetIndexForPath(path) >= 0;
+}
+
+const DeltaArchiveManifest_File& DeltaDiffParser::GetFileAtPath(
+    const string& path) const {
+  int idx = GetIndexForPath(path);
+  CHECK_GE(idx, 0) << path;
+  return archive_.files(idx);
+}
+
+// Returns -1 if not found.
+int DeltaDiffParser::GetIndexOfFileChild(
+    const DeltaArchiveManifest_File& file, const string& child_name) const {
+  if (file.children_size() == 0)
+    return -1;
+  int begin = 0;
+  int end = file.children_size();
+  while (begin < end) {
+    int middle = (begin + end) / 2;
+    const string& middle_name = file.children(middle).name();
+    int cmp_result = strcmp(middle_name.c_str(), child_name.c_str());
+    if (cmp_result == 0)
+      return file.children(middle).index();
+    if (cmp_result < 0)
+      begin = middle + 1;
+    else
+      end = middle;
+  }
+  return -1;
+}
+
+// Converts a path to an index in archive_. It does this by separating
+// the path components and going from root to leaf, finding the
+// File message for each component. Index values for children are
+// stored in File messages.
+int DeltaDiffParser::GetIndexForPath(const string& path) const {
+  string cleaned_path = utils::NormalizePath(path, true);
+  // strip leading slash
+  if (cleaned_path[0] == '/')
+    cleaned_path = cleaned_path.c_str() + 1;
+  if (cleaned_path.empty())
+    return 0;
+  string::size_type begin = 0;
+  string::size_type end = cleaned_path.find_first_of('/', begin + 1);
+  const DeltaArchiveManifest_File* file = &archive_.files(0);
+  int file_idx = -1;
+  for (;;) {
+    string component = cleaned_path.substr(begin, end - begin);
+    if (component.empty())
+      break;
+    // search for component in 'file'
+    file_idx = GetIndexOfFileChild(*file, component);
+    if (file_idx < 0)
+      return file_idx;
+    file = &archive_.files(file_idx);
+    if (end == string::npos)
+      break;
+    begin = end + 1;
+    end = cleaned_path.find_first_of('/', begin + 1);
+  }
+  return file_idx;
+}
+
+bool DeltaDiffParser::ReadDataVector(off_t offset, off_t length,
+                                     std::vector<char>* out) const {
+  out->resize(static_cast<vector<char>::size_type>(length));
+  int r = pread(fd_, &((*out)[0]), length, offset);
+  TEST_AND_RETURN_FALSE_ERRNO(r >= 0);
+  return true;
+}
+
+bool DeltaDiffParser::CopyDataToFile(off_t offset, off_t length,
+                                     bool should_decompress,
+                                     const std::string& path) const {
+  DirectFileWriter direct_writer;
+  GzipDecompressingFileWriter decompressing_writer(&direct_writer);
+  FileWriter* writer = NULL;  // will point to one of the two writers above
+
+  writer = (should_decompress ?
+            static_cast<FileWriter*>(&decompressing_writer) :
+            static_cast<FileWriter*>(&direct_writer));
+  ScopedFileWriterCloser closer(writer);
+
+  int r = writer->Open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0644);
+  TEST_AND_RETURN_FALSE(r == 0);
+
+  off_t bytes_transferred = 0;
+
+  while (bytes_transferred < length) {
+    char buf[kCopyFileBufferSize];
+    size_t bytes_to_read = min(length - bytes_transferred,
+                               static_cast<off_t>(sizeof(buf)));
+    ssize_t bytes_read = pread(fd_, buf, bytes_to_read,
+                               offset + bytes_transferred);
+    if (bytes_read == 0)
+      break;  // EOF
+    TEST_AND_RETURN_FALSE_ERRNO(bytes_read > 0);
+    int bytes_written = writer->Write(buf, bytes_read);
+    TEST_AND_RETURN_FALSE(bytes_written == bytes_read);
+    bytes_transferred += bytes_written;
+  }
+  TEST_AND_RETURN_FALSE(bytes_transferred == length);
+  LOG_IF(ERROR, bytes_transferred > length) << "Wrote too many bytes(?)";
+  return true;
+}
+
+
+const DeltaDiffParser::Iterator DeltaDiffParser::Begin() {
+  DeltaDiffParserIterator ret(&archive_);
+  ret.path_indices_.push_back(0);
+  return ret;
+}
+
+const DeltaDiffParser::Iterator DeltaDiffParser::End() {
+  return DeltaDiffParserIterator(&archive_);
+}
+
+}  // namespace chromeos_update_engine