1 // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "puffin/src/include/puffin/puffdiff.h" 6 7 #include <endian.h> 8 #include <inttypes.h> 9 #include <unistd.h> 10 11 #include <string> 12 #include <vector> 13 14 #include "bsdiff/bsdiff.h" 15 16 #include "puffin/src/include/puffin/common.h" 17 #include "puffin/src/include/puffin/puffer.h" 18 #include "puffin/src/include/puffin/puffpatch.h" 19 #include "puffin/src/include/puffin/utils.h" 20 #include "puffin/src/file_stream.h" 21 #include "puffin/src/memory_stream.h" 22 #include "puffin/src/puffin.pb.h" 23 #include "puffin/src/puffin_stream.h" 24 #include "puffin/src/set_errors.h" 25 26 namespace puffin { 27 28 using std::string; 29 using std::vector; 30 31 namespace { 32 33 template <typename T> 34 void CopyVectorToRpf( 35 const T& from, 36 google::protobuf::RepeatedPtrField<metadata::BitExtent>* to, 37 size_t coef) { 38 to->Reserve(from.size()); 39 for (const auto& ext : from) { 40 auto tmp = to->Add(); 41 tmp->set_offset(ext.offset * coef); 42 tmp->set_length(ext.length * coef); 43 } 44 } 45 46 // Structure of a puffin patch 47 // +-------+------------------+-------------+--------------+ 48 // |P|U|F|1| PatchHeader Size | PatchHeader | bsdiff_patch | 49 // +-------+------------------+-------------+--------------+ 50 bool CreatePatch(const Buffer& bsdiff_patch, 51 const vector<BitExtent>& src_deflates, 52 const vector<BitExtent>& dst_deflates, 53 const vector<ByteExtent>& src_puffs, 54 const vector<ByteExtent>& dst_puffs, 55 uint64_t src_puff_size, 56 uint64_t dst_puff_size, 57 Buffer* patch) { 58 metadata::PatchHeader header; 59 header.set_version(1); 60 61 CopyVectorToRpf(src_deflates, header.mutable_src()->mutable_deflates(), 1); 62 CopyVectorToRpf(dst_deflates, header.mutable_dst()->mutable_deflates(), 1); 63 CopyVectorToRpf(src_puffs, header.mutable_src()->mutable_puffs(), 8); 64 CopyVectorToRpf(dst_puffs, header.mutable_dst()->mutable_puffs(), 8); 65 66 header.mutable_src()->set_puff_length(src_puff_size); 67 header.mutable_dst()->set_puff_length(dst_puff_size); 68 69 const uint32_t header_size = header.ByteSize(); 70 71 uint64_t offset = 0; 72 patch->resize(kMagicLength + sizeof(header_size) + header_size + 73 bsdiff_patch.size()); 74 75 memcpy(patch->data() + offset, kMagic, kMagicLength); 76 offset += kMagicLength; 77 78 // Read header size from big-endian mode. 79 uint32_t be_header_size = htobe32(header_size); 80 memcpy(patch->data() + offset, &be_header_size, sizeof(be_header_size)); 81 offset += 4; 82 83 TEST_AND_RETURN_FALSE( 84 header.SerializeToArray(patch->data() + offset, header_size)); 85 offset += header_size; 86 87 memcpy(patch->data() + offset, bsdiff_patch.data(), bsdiff_patch.size()); 88 89 if (bsdiff_patch.size() > patch->size()) { 90 LOG(ERROR) << "Puffin patch is invalid"; 91 } 92 return true; 93 } 94 95 } // namespace 96 97 bool PuffDiff(UniqueStreamPtr src, 98 UniqueStreamPtr dst, 99 const vector<BitExtent>& src_deflates, 100 const vector<BitExtent>& dst_deflates, 101 const string& tmp_filepath, 102 Buffer* patch) { 103 auto puffer = std::make_shared<Puffer>(); 104 auto puff_deflate_stream = 105 [&puffer](UniqueStreamPtr stream, const vector<BitExtent>& deflates, 106 Buffer* puff_buffer, vector<ByteExtent>* puffs) { 107 uint64_t puff_size; 108 TEST_AND_RETURN_FALSE(stream->Seek(0)); 109 TEST_AND_RETURN_FALSE( 110 FindPuffLocations(stream, deflates, puffs, &puff_size)); 111 TEST_AND_RETURN_FALSE(stream->Seek(0)); 112 auto src_puffin_stream = PuffinStream::CreateForPuff( 113 std::move(stream), puffer, puff_size, deflates, *puffs); 114 puff_buffer->resize(puff_size); 115 TEST_AND_RETURN_FALSE( 116 src_puffin_stream->Read(puff_buffer->data(), puff_buffer->size())); 117 return true; 118 }; 119 120 Buffer src_puff_buffer; 121 Buffer dst_puff_buffer; 122 vector<ByteExtent> src_puffs, dst_puffs; 123 TEST_AND_RETURN_FALSE(puff_deflate_stream(std::move(src), src_deflates, 124 &src_puff_buffer, &src_puffs)); 125 TEST_AND_RETURN_FALSE(puff_deflate_stream(std::move(dst), dst_deflates, 126 &dst_puff_buffer, &dst_puffs)); 127 128 TEST_AND_RETURN_FALSE( 129 0 == bsdiff::bsdiff(src_puff_buffer.data(), src_puff_buffer.size(), 130 dst_puff_buffer.data(), dst_puff_buffer.size(), 131 tmp_filepath.c_str(), nullptr)); 132 133 auto bsdiff_patch = FileStream::Open(tmp_filepath, true, false); 134 TEST_AND_RETURN_FALSE(bsdiff_patch); 135 uint64_t patch_size; 136 TEST_AND_RETURN_FALSE(bsdiff_patch->GetSize(&patch_size)); 137 Buffer bsdiff_patch_buf(patch_size); 138 TEST_AND_RETURN_FALSE( 139 bsdiff_patch->Read(bsdiff_patch_buf.data(), bsdiff_patch_buf.size())); 140 TEST_AND_RETURN_FALSE(bsdiff_patch->Close()); 141 142 TEST_AND_RETURN_FALSE(CreatePatch( 143 bsdiff_patch_buf, src_deflates, dst_deflates, src_puffs, dst_puffs, 144 src_puff_buffer.size(), dst_puff_buffer.size(), patch)); 145 return true; 146 } 147 148 bool PuffDiff(const Buffer& src, 149 const Buffer& dst, 150 const vector<BitExtent>& src_deflates, 151 const vector<BitExtent>& dst_deflates, 152 const string& tmp_filepath, 153 Buffer* patch) { 154 return PuffDiff(MemoryStream::CreateForRead(src), 155 MemoryStream::CreateForRead(dst), src_deflates, dst_deflates, 156 tmp_filepath, patch); 157 } 158 159 } // namespace puffin 160