// Copyright 2017 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 "puffin/src/include/puffin/puffdiff.h" #include #include #include #include #include #include "bsdiff/bsdiff.h" #include "bsdiff/patch_writer_factory.h" #include "puffin/src/file_stream.h" #include "puffin/src/include/puffin/common.h" #include "puffin/src/include/puffin/puffer.h" #include "puffin/src/include/puffin/puffpatch.h" #include "puffin/src/include/puffin/utils.h" #include "puffin/src/logging.h" #include "puffin/src/memory_stream.h" #include "puffin/src/puffin.pb.h" #include "puffin/src/puffin_stream.h" using std::string; using std::vector; namespace puffin { namespace { const int kBrotliCompressionQuality = 11; template void CopyVectorToRpf( const T& from, google::protobuf::RepeatedPtrField* to, size_t coef) { to->Reserve(from.size()); for (const auto& ext : from) { auto tmp = to->Add(); tmp->set_offset(ext.offset * coef); tmp->set_length(ext.length * coef); } } // Structure of a puffin patch // +-------+------------------+-------------+--------------+ // |P|U|F|1| PatchHeader Size | PatchHeader | bsdiff_patch | // +-------+------------------+-------------+--------------+ bool CreatePatch(const Buffer& bsdiff_patch, const vector& src_deflates, const vector& dst_deflates, const vector& src_puffs, const vector& dst_puffs, uint64_t src_puff_size, uint64_t dst_puff_size, Buffer* patch) { metadata::PatchHeader header; header.set_version(1); CopyVectorToRpf(src_deflates, header.mutable_src()->mutable_deflates(), 1); CopyVectorToRpf(dst_deflates, header.mutable_dst()->mutable_deflates(), 1); CopyVectorToRpf(src_puffs, header.mutable_src()->mutable_puffs(), 8); CopyVectorToRpf(dst_puffs, header.mutable_dst()->mutable_puffs(), 8); header.mutable_src()->set_puff_length(src_puff_size); header.mutable_dst()->set_puff_length(dst_puff_size); const size_t header_size_long = header.ByteSizeLong(); TEST_AND_RETURN_FALSE(header_size_long <= UINT32_MAX); const uint32_t header_size = header_size_long; uint64_t offset = 0; patch->resize(kMagicLength + sizeof(header_size) + header_size + bsdiff_patch.size()); memcpy(patch->data() + offset, kMagic, kMagicLength); offset += kMagicLength; // Read header size from big-endian mode. uint32_t be_header_size = htobe32(header_size); memcpy(patch->data() + offset, &be_header_size, sizeof(be_header_size)); offset += 4; TEST_AND_RETURN_FALSE( header.SerializeToArray(patch->data() + offset, header_size)); offset += header_size; memcpy(patch->data() + offset, bsdiff_patch.data(), bsdiff_patch.size()); if (bsdiff_patch.size() > patch->size()) { LOG(ERROR) << "Puffin patch is invalid"; } return true; } } // namespace bool PuffDiff(UniqueStreamPtr src, UniqueStreamPtr dst, const vector& src_deflates, const vector& dst_deflates, const vector& compressors, const string& tmp_filepath, Buffer* patch) { auto puffer = std::make_shared(); auto puff_deflate_stream = [&puffer](UniqueStreamPtr stream, const vector& deflates, Buffer* puff_buffer, vector* puffs) { uint64_t puff_size; TEST_AND_RETURN_FALSE(stream->Seek(0)); TEST_AND_RETURN_FALSE( FindPuffLocations(stream, deflates, puffs, &puff_size)); TEST_AND_RETURN_FALSE(stream->Seek(0)); auto src_puffin_stream = PuffinStream::CreateForPuff( std::move(stream), puffer, puff_size, deflates, *puffs); puff_buffer->resize(puff_size); TEST_AND_RETURN_FALSE( src_puffin_stream->Read(puff_buffer->data(), puff_buffer->size())); return true; }; Buffer src_puff_buffer; Buffer dst_puff_buffer; vector src_puffs, dst_puffs; TEST_AND_RETURN_FALSE(puff_deflate_stream(std::move(src), src_deflates, &src_puff_buffer, &src_puffs)); TEST_AND_RETURN_FALSE(puff_deflate_stream(std::move(dst), dst_deflates, &dst_puff_buffer, &dst_puffs)); auto bsdiff_patch_writer = bsdiff::CreateBSDF2PatchWriter( tmp_filepath, compressors, kBrotliCompressionQuality); TEST_AND_RETURN_FALSE( 0 == bsdiff::bsdiff(src_puff_buffer.data(), src_puff_buffer.size(), dst_puff_buffer.data(), dst_puff_buffer.size(), bsdiff_patch_writer.get(), nullptr)); auto bsdiff_patch = FileStream::Open(tmp_filepath, true, false); TEST_AND_RETURN_FALSE(bsdiff_patch); uint64_t patch_size; TEST_AND_RETURN_FALSE(bsdiff_patch->GetSize(&patch_size)); Buffer bsdiff_patch_buf(patch_size); TEST_AND_RETURN_FALSE( bsdiff_patch->Read(bsdiff_patch_buf.data(), bsdiff_patch_buf.size())); TEST_AND_RETURN_FALSE(bsdiff_patch->Close()); TEST_AND_RETURN_FALSE(CreatePatch( bsdiff_patch_buf, src_deflates, dst_deflates, src_puffs, dst_puffs, src_puff_buffer.size(), dst_puff_buffer.size(), patch)); return true; } bool PuffDiff(const Buffer& src, const Buffer& dst, const vector& src_deflates, const vector& dst_deflates, const vector& compressors, const string& tmp_filepath, Buffer* patch) { return PuffDiff(MemoryStream::CreateForRead(src), MemoryStream::CreateForRead(dst), src_deflates, dst_deflates, compressors, tmp_filepath, patch); } bool PuffDiff(const Buffer& src, const Buffer& dst, const vector& src_deflates, const vector& dst_deflates, const string& tmp_filepath, Buffer* patch) { return PuffDiff( src, dst, src_deflates, dst_deflates, {bsdiff::CompressorType::kBZ2, bsdiff::CompressorType::kBrotli}, tmp_filepath, patch); } } // namespace puffin