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 #include "bsdiff/patch_writer_factory.h"
16
17 #include "puffin/src/file_stream.h"
18 #include "puffin/src/include/puffin/common.h"
19 #include "puffin/src/include/puffin/puffer.h"
20 #include "puffin/src/include/puffin/puffpatch.h"
21 #include "puffin/src/include/puffin/utils.h"
22 #include "puffin/src/logging.h"
23 #include "puffin/src/memory_stream.h"
24 #include "puffin/src/puffin.pb.h"
25 #include "puffin/src/puffin_stream.h"
26
27 using std::string;
28 using std::vector;
29
30 namespace puffin {
31
32 namespace {
33 const int kBrotliCompressionQuality = 11;
34
35 template <typename T>
CopyVectorToRpf(const T & from,google::protobuf::RepeatedPtrField<metadata::BitExtent> * to,size_t coef)36 void CopyVectorToRpf(
37 const T& from,
38 google::protobuf::RepeatedPtrField<metadata::BitExtent>* to,
39 size_t coef) {
40 to->Reserve(from.size());
41 for (const auto& ext : from) {
42 auto tmp = to->Add();
43 tmp->set_offset(ext.offset * coef);
44 tmp->set_length(ext.length * coef);
45 }
46 }
47
48 // Structure of a puffin patch
49 // +-------+------------------+-------------+--------------+
50 // |P|U|F|1| PatchHeader Size | PatchHeader | bsdiff_patch |
51 // +-------+------------------+-------------+--------------+
CreatePatch(const Buffer & bsdiff_patch,const vector<BitExtent> & src_deflates,const vector<BitExtent> & dst_deflates,const vector<ByteExtent> & src_puffs,const vector<ByteExtent> & dst_puffs,uint64_t src_puff_size,uint64_t dst_puff_size,Buffer * patch)52 bool CreatePatch(const Buffer& bsdiff_patch,
53 const vector<BitExtent>& src_deflates,
54 const vector<BitExtent>& dst_deflates,
55 const vector<ByteExtent>& src_puffs,
56 const vector<ByteExtent>& dst_puffs,
57 uint64_t src_puff_size,
58 uint64_t dst_puff_size,
59 Buffer* patch) {
60 metadata::PatchHeader header;
61 header.set_version(1);
62
63 CopyVectorToRpf(src_deflates, header.mutable_src()->mutable_deflates(), 1);
64 CopyVectorToRpf(dst_deflates, header.mutable_dst()->mutable_deflates(), 1);
65 CopyVectorToRpf(src_puffs, header.mutable_src()->mutable_puffs(), 8);
66 CopyVectorToRpf(dst_puffs, header.mutable_dst()->mutable_puffs(), 8);
67
68 header.mutable_src()->set_puff_length(src_puff_size);
69 header.mutable_dst()->set_puff_length(dst_puff_size);
70
71 const size_t header_size_long = header.ByteSizeLong();
72 TEST_AND_RETURN_FALSE(header_size_long <= UINT32_MAX);
73 const uint32_t header_size = header_size_long;
74
75 uint64_t offset = 0;
76 patch->resize(kMagicLength + sizeof(header_size) + header_size +
77 bsdiff_patch.size());
78
79 memcpy(patch->data() + offset, kMagic, kMagicLength);
80 offset += kMagicLength;
81
82 // Read header size from big-endian mode.
83 uint32_t be_header_size = htobe32(header_size);
84 memcpy(patch->data() + offset, &be_header_size, sizeof(be_header_size));
85 offset += 4;
86
87 TEST_AND_RETURN_FALSE(
88 header.SerializeToArray(patch->data() + offset, header_size));
89 offset += header_size;
90
91 memcpy(patch->data() + offset, bsdiff_patch.data(), bsdiff_patch.size());
92
93 if (bsdiff_patch.size() > patch->size()) {
94 LOG(ERROR) << "Puffin patch is invalid";
95 }
96 return true;
97 }
98
99 } // namespace
100
PuffDiff(UniqueStreamPtr src,UniqueStreamPtr dst,const vector<BitExtent> & src_deflates,const vector<BitExtent> & dst_deflates,const vector<bsdiff::CompressorType> & compressors,const string & tmp_filepath,Buffer * patch)101 bool PuffDiff(UniqueStreamPtr src,
102 UniqueStreamPtr dst,
103 const vector<BitExtent>& src_deflates,
104 const vector<BitExtent>& dst_deflates,
105 const vector<bsdiff::CompressorType>& compressors,
106 const string& tmp_filepath,
107 Buffer* patch) {
108 auto puffer = std::make_shared<Puffer>();
109 auto puff_deflate_stream =
110 [&puffer](UniqueStreamPtr stream, const vector<BitExtent>& deflates,
111 Buffer* puff_buffer, vector<ByteExtent>* puffs) {
112 uint64_t puff_size;
113 TEST_AND_RETURN_FALSE(stream->Seek(0));
114 TEST_AND_RETURN_FALSE(
115 FindPuffLocations(stream, deflates, puffs, &puff_size));
116 TEST_AND_RETURN_FALSE(stream->Seek(0));
117 auto src_puffin_stream = PuffinStream::CreateForPuff(
118 std::move(stream), puffer, puff_size, deflates, *puffs);
119 puff_buffer->resize(puff_size);
120 TEST_AND_RETURN_FALSE(
121 src_puffin_stream->Read(puff_buffer->data(), puff_buffer->size()));
122 return true;
123 };
124
125 Buffer src_puff_buffer;
126 Buffer dst_puff_buffer;
127 vector<ByteExtent> src_puffs, dst_puffs;
128 TEST_AND_RETURN_FALSE(puff_deflate_stream(std::move(src), src_deflates,
129 &src_puff_buffer, &src_puffs));
130 TEST_AND_RETURN_FALSE(puff_deflate_stream(std::move(dst), dst_deflates,
131 &dst_puff_buffer, &dst_puffs));
132
133 auto bsdiff_patch_writer = bsdiff::CreateBSDF2PatchWriter(
134 tmp_filepath, compressors, kBrotliCompressionQuality);
135
136 TEST_AND_RETURN_FALSE(
137 0 == bsdiff::bsdiff(src_puff_buffer.data(), src_puff_buffer.size(),
138 dst_puff_buffer.data(), dst_puff_buffer.size(),
139 bsdiff_patch_writer.get(), nullptr));
140
141 auto bsdiff_patch = FileStream::Open(tmp_filepath, true, false);
142 TEST_AND_RETURN_FALSE(bsdiff_patch);
143 uint64_t patch_size;
144 TEST_AND_RETURN_FALSE(bsdiff_patch->GetSize(&patch_size));
145 Buffer bsdiff_patch_buf(patch_size);
146 TEST_AND_RETURN_FALSE(
147 bsdiff_patch->Read(bsdiff_patch_buf.data(), bsdiff_patch_buf.size()));
148 TEST_AND_RETURN_FALSE(bsdiff_patch->Close());
149
150 TEST_AND_RETURN_FALSE(CreatePatch(
151 bsdiff_patch_buf, src_deflates, dst_deflates, src_puffs, dst_puffs,
152 src_puff_buffer.size(), dst_puff_buffer.size(), patch));
153 return true;
154 }
155
PuffDiff(const Buffer & src,const Buffer & dst,const vector<BitExtent> & src_deflates,const vector<BitExtent> & dst_deflates,const vector<bsdiff::CompressorType> & compressors,const string & tmp_filepath,Buffer * patch)156 bool PuffDiff(const Buffer& src,
157 const Buffer& dst,
158 const vector<BitExtent>& src_deflates,
159 const vector<BitExtent>& dst_deflates,
160 const vector<bsdiff::CompressorType>& compressors,
161 const string& tmp_filepath,
162 Buffer* patch) {
163 return PuffDiff(MemoryStream::CreateForRead(src),
164 MemoryStream::CreateForRead(dst), src_deflates, dst_deflates,
165 compressors, tmp_filepath, patch);
166 }
167
PuffDiff(const Buffer & src,const Buffer & dst,const vector<BitExtent> & src_deflates,const vector<BitExtent> & dst_deflates,const string & tmp_filepath,Buffer * patch)168 bool PuffDiff(const Buffer& src,
169 const Buffer& dst,
170 const vector<BitExtent>& src_deflates,
171 const vector<BitExtent>& dst_deflates,
172 const string& tmp_filepath,
173 Buffer* patch) {
174 return PuffDiff(
175 src, dst, src_deflates, dst_deflates,
176 {bsdiff::CompressorType::kBZ2, bsdiff::CompressorType::kBrotli},
177 tmp_filepath, patch);
178 }
179
180 } // namespace puffin
181