Index: src/platform/update_engine/delta_diff_parser.cc |
diff --git a/src/platform/update_engine/delta_diff_parser.cc b/src/platform/update_engine/delta_diff_parser.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..47c763f5195efffcb1feb31cde8188cd31b261e3 |
--- /dev/null |
+++ b/src/platform/update_engine/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 |