diff options
author | adlr@google.com <adlr@google.com@06c00378-0e64-4dae-be16-12b19f9950a1> | 2009-12-04 20:57:17 +0000 |
---|---|---|
committer | adlr@google.com <adlr@google.com@06c00378-0e64-4dae-be16-12b19f9950a1> | 2009-12-04 20:57:17 +0000 |
commit | 3defe6acb3609e70e851a6eff062577d25a2af9d (patch) | |
tree | 341e979027fde117dd8906483db7a5c703a2e1cf /delta_diff_parser.cc | |
parent | c98a7edf648aad88b3f66df3b5a7d43d6a6d7fa9 (diff) |
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
Diffstat (limited to 'delta_diff_parser.cc')
-rw-r--r-- | delta_diff_parser.cc | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/delta_diff_parser.cc b/delta_diff_parser.cc new file mode 100644 index 00000000..47c763f5 --- /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 |