1*a3a45f30SXin Li // Copyright 2017 The Chromium OS Authors. All rights reserved.
2*a3a45f30SXin Li // Use of this source code is governed by a BSD-style license that can be
3*a3a45f30SXin Li // found in the LICENSE file.
4*a3a45f30SXin Li
5*a3a45f30SXin Li #include "bsdiff/patch_writer.h"
6*a3a45f30SXin Li
7*a3a45f30SXin Li #include <string.h>
8*a3a45f30SXin Li #include <limits>
9*a3a45f30SXin Li
10*a3a45f30SXin Li #include "bsdiff/brotli_compressor.h"
11*a3a45f30SXin Li #include "bsdiff/bz2_compressor.h"
12*a3a45f30SXin Li #include "bsdiff/constants.h"
13*a3a45f30SXin Li #include "bsdiff/control_entry.h"
14*a3a45f30SXin Li #include "bsdiff/logging.h"
15*a3a45f30SXin Li
16*a3a45f30SXin Li
17*a3a45f30SXin Li namespace bsdiff {
18*a3a45f30SXin Li
BsdiffPatchWriter(const std::string & patch_filename)19*a3a45f30SXin Li BsdiffPatchWriter::BsdiffPatchWriter(const std::string& patch_filename)
20*a3a45f30SXin Li : patch_filename_(patch_filename),
21*a3a45f30SXin Li format_(BsdiffFormat::kLegacy),
22*a3a45f30SXin Li brotli_quality_(-1) {
23*a3a45f30SXin Li types_.emplace_back(CompressorType::kBZ2);
24*a3a45f30SXin Li }
25*a3a45f30SXin Li
26*a3a45f30SXin Li
BsdiffPatchWriter(const std::string & patch_filename,const std::vector<CompressorType> & types,int brotli_quality)27*a3a45f30SXin Li BsdiffPatchWriter::BsdiffPatchWriter(const std::string& patch_filename,
28*a3a45f30SXin Li const std::vector<CompressorType>& types,
29*a3a45f30SXin Li int brotli_quality)
30*a3a45f30SXin Li : patch_filename_(patch_filename),
31*a3a45f30SXin Li format_(BsdiffFormat::kBsdf2),
32*a3a45f30SXin Li types_(types),
33*a3a45f30SXin Li brotli_quality_(brotli_quality) {}
34*a3a45f30SXin Li
InitializeCompressorList(std::vector<std::unique_ptr<bsdiff::CompressorInterface>> * compressor_list)35*a3a45f30SXin Li bool BsdiffPatchWriter::InitializeCompressorList(
36*a3a45f30SXin Li std::vector<std::unique_ptr<bsdiff::CompressorInterface>>*
37*a3a45f30SXin Li compressor_list) {
38*a3a45f30SXin Li if (types_.empty()) {
39*a3a45f30SXin Li LOG(ERROR) << "Patch writer expects at least one compressor.";
40*a3a45f30SXin Li return false;
41*a3a45f30SXin Li }
42*a3a45f30SXin Li compressor_list->clear();
43*a3a45f30SXin Li
44*a3a45f30SXin Li for (const auto& type : types_) {
45*a3a45f30SXin Li switch (type) {
46*a3a45f30SXin Li case CompressorType::kBZ2:
47*a3a45f30SXin Li compressor_list->emplace_back(new BZ2Compressor());
48*a3a45f30SXin Li break;
49*a3a45f30SXin Li case CompressorType::kBrotli:
50*a3a45f30SXin Li compressor_list->emplace_back(new BrotliCompressor(brotli_quality_));
51*a3a45f30SXin Li break;
52*a3a45f30SXin Li case CompressorType::kNoCompression:
53*a3a45f30SXin Li LOG(ERROR) << "Unsupported compression type " << static_cast<int>(type);
54*a3a45f30SXin Li return false;
55*a3a45f30SXin Li }
56*a3a45f30SXin Li }
57*a3a45f30SXin Li
58*a3a45f30SXin Li for (const auto& compressor : *compressor_list) {
59*a3a45f30SXin Li if (!compressor) {
60*a3a45f30SXin Li return false;
61*a3a45f30SXin Li }
62*a3a45f30SXin Li }
63*a3a45f30SXin Li
64*a3a45f30SXin Li return true;
65*a3a45f30SXin Li }
66*a3a45f30SXin Li
Init(size_t)67*a3a45f30SXin Li bool BsdiffPatchWriter::Init(size_t /* new_size */) {
68*a3a45f30SXin Li if (!InitializeCompressorList(&ctrl_stream_list_)) {
69*a3a45f30SXin Li LOG(ERROR) << "Failed to initialize control stream compressors.";
70*a3a45f30SXin Li return false;
71*a3a45f30SXin Li }
72*a3a45f30SXin Li
73*a3a45f30SXin Li if (!InitializeCompressorList(&diff_stream_list_)) {
74*a3a45f30SXin Li LOG(ERROR) << "Failed to initialize diff stream compressors.";
75*a3a45f30SXin Li return false;
76*a3a45f30SXin Li }
77*a3a45f30SXin Li
78*a3a45f30SXin Li if (!InitializeCompressorList(&extra_stream_list_)) {
79*a3a45f30SXin Li LOG(ERROR) << "Failed to initialize extra stream compressors.";
80*a3a45f30SXin Li return false;
81*a3a45f30SXin Li }
82*a3a45f30SXin Li
83*a3a45f30SXin Li fp_ = fopen(patch_filename_.c_str(), "w");
84*a3a45f30SXin Li if (!fp_) {
85*a3a45f30SXin Li LOG(ERROR) << "Opening " << patch_filename_;
86*a3a45f30SXin Li return false;
87*a3a45f30SXin Li }
88*a3a45f30SXin Li return true;
89*a3a45f30SXin Li }
90*a3a45f30SXin Li
WriteDiffStream(const uint8_t * data,size_t size)91*a3a45f30SXin Li bool BsdiffPatchWriter::WriteDiffStream(const uint8_t* data, size_t size) {
92*a3a45f30SXin Li for (const auto& compressor : diff_stream_list_) {
93*a3a45f30SXin Li if (!compressor->Write(data, size)) {
94*a3a45f30SXin Li return false;
95*a3a45f30SXin Li }
96*a3a45f30SXin Li }
97*a3a45f30SXin Li
98*a3a45f30SXin Li return true;
99*a3a45f30SXin Li }
100*a3a45f30SXin Li
WriteExtraStream(const uint8_t * data,size_t size)101*a3a45f30SXin Li bool BsdiffPatchWriter::WriteExtraStream(const uint8_t* data, size_t size) {
102*a3a45f30SXin Li for (const auto& compressor : extra_stream_list_) {
103*a3a45f30SXin Li if (!compressor->Write(data, size)) {
104*a3a45f30SXin Li return false;
105*a3a45f30SXin Li }
106*a3a45f30SXin Li }
107*a3a45f30SXin Li
108*a3a45f30SXin Li return true;
109*a3a45f30SXin Li }
110*a3a45f30SXin Li
AddControlEntry(const ControlEntry & entry)111*a3a45f30SXin Li bool BsdiffPatchWriter::AddControlEntry(const ControlEntry& entry) {
112*a3a45f30SXin Li // Generate the 24 byte control entry.
113*a3a45f30SXin Li uint8_t buf[24];
114*a3a45f30SXin Li EncodeInt64(entry.diff_size, buf);
115*a3a45f30SXin Li EncodeInt64(entry.extra_size, buf + 8);
116*a3a45f30SXin Li EncodeInt64(entry.offset_increment, buf + 16);
117*a3a45f30SXin Li
118*a3a45f30SXin Li for (const auto& compressor : ctrl_stream_list_) {
119*a3a45f30SXin Li if (!compressor->Write(buf, sizeof(buf))) {
120*a3a45f30SXin Li return false;
121*a3a45f30SXin Li }
122*a3a45f30SXin Li }
123*a3a45f30SXin Li
124*a3a45f30SXin Li written_output_ += entry.diff_size + entry.extra_size;
125*a3a45f30SXin Li return true;
126*a3a45f30SXin Li }
127*a3a45f30SXin Li
SelectSmallestResult(const std::vector<std::unique_ptr<CompressorInterface>> & compressor_list,CompressorInterface ** smallest_compressor)128*a3a45f30SXin Li bool BsdiffPatchWriter::SelectSmallestResult(
129*a3a45f30SXin Li const std::vector<std::unique_ptr<CompressorInterface>>& compressor_list,
130*a3a45f30SXin Li CompressorInterface** smallest_compressor) {
131*a3a45f30SXin Li size_t min_size = std::numeric_limits<size_t>::max();
132*a3a45f30SXin Li for (const auto& compressor : compressor_list) {
133*a3a45f30SXin Li if (!compressor->Finish()) {
134*a3a45f30SXin Li LOG(ERROR) << "Failed to finalize compressed streams.";
135*a3a45f30SXin Li return false;
136*a3a45f30SXin Li }
137*a3a45f30SXin Li
138*a3a45f30SXin Li // Update |smallest_compressor| if the current compressor produces a
139*a3a45f30SXin Li // smaller result.
140*a3a45f30SXin Li if (compressor->GetCompressedData().size() < min_size) {
141*a3a45f30SXin Li min_size = compressor->GetCompressedData().size();
142*a3a45f30SXin Li *smallest_compressor = compressor.get();
143*a3a45f30SXin Li }
144*a3a45f30SXin Li }
145*a3a45f30SXin Li
146*a3a45f30SXin Li return true;
147*a3a45f30SXin Li }
148*a3a45f30SXin Li
Close()149*a3a45f30SXin Li bool BsdiffPatchWriter::Close() {
150*a3a45f30SXin Li if (!fp_) {
151*a3a45f30SXin Li LOG(ERROR) << "File not open.";
152*a3a45f30SXin Li return false;
153*a3a45f30SXin Li }
154*a3a45f30SXin Li
155*a3a45f30SXin Li CompressorInterface* ctrl_stream = nullptr;
156*a3a45f30SXin Li if (!SelectSmallestResult(ctrl_stream_list_, &ctrl_stream) || !ctrl_stream) {
157*a3a45f30SXin Li return false;
158*a3a45f30SXin Li }
159*a3a45f30SXin Li
160*a3a45f30SXin Li CompressorInterface* diff_stream = nullptr;
161*a3a45f30SXin Li if (!SelectSmallestResult(diff_stream_list_, &diff_stream) || !diff_stream) {
162*a3a45f30SXin Li return false;
163*a3a45f30SXin Li }
164*a3a45f30SXin Li
165*a3a45f30SXin Li CompressorInterface* extra_stream = nullptr;
166*a3a45f30SXin Li if (!SelectSmallestResult(extra_stream_list_, &extra_stream) ||
167*a3a45f30SXin Li !extra_stream) {
168*a3a45f30SXin Li return false;
169*a3a45f30SXin Li }
170*a3a45f30SXin Li
171*a3a45f30SXin Li auto ctrl_data = ctrl_stream->GetCompressedData();
172*a3a45f30SXin Li auto diff_data = diff_stream->GetCompressedData();
173*a3a45f30SXin Li auto extra_data = extra_stream->GetCompressedData();
174*a3a45f30SXin Li
175*a3a45f30SXin Li uint8_t types[3] = {static_cast<uint8_t>(ctrl_stream->Type()),
176*a3a45f30SXin Li static_cast<uint8_t>(diff_stream->Type()),
177*a3a45f30SXin Li static_cast<uint8_t>(extra_stream->Type())};
178*a3a45f30SXin Li
179*a3a45f30SXin Li if (!WriteHeader(types, ctrl_data.size(), diff_data.size()))
180*a3a45f30SXin Li return false;
181*a3a45f30SXin Li
182*a3a45f30SXin Li if (fwrite(ctrl_data.data(), 1, ctrl_data.size(), fp_) != ctrl_data.size()) {
183*a3a45f30SXin Li LOG(ERROR) << "Writing ctrl_data.";
184*a3a45f30SXin Li return false;
185*a3a45f30SXin Li }
186*a3a45f30SXin Li if (fwrite(diff_data.data(), 1, diff_data.size(), fp_) != diff_data.size()) {
187*a3a45f30SXin Li LOG(ERROR) << "Writing diff_data.";
188*a3a45f30SXin Li return false;
189*a3a45f30SXin Li }
190*a3a45f30SXin Li if (fwrite(extra_data.data(), 1, extra_data.size(), fp_) !=
191*a3a45f30SXin Li extra_data.size()) {
192*a3a45f30SXin Li LOG(ERROR) << "Writing extra_data.";
193*a3a45f30SXin Li return false;
194*a3a45f30SXin Li }
195*a3a45f30SXin Li if (fclose(fp_) != 0) {
196*a3a45f30SXin Li LOG(ERROR) << "Closing the patch file.";
197*a3a45f30SXin Li return false;
198*a3a45f30SXin Li }
199*a3a45f30SXin Li fp_ = nullptr;
200*a3a45f30SXin Li return true;
201*a3a45f30SXin Li }
202*a3a45f30SXin Li
WriteHeader(uint8_t types[3],uint64_t ctrl_size,uint64_t diff_size)203*a3a45f30SXin Li bool BsdiffPatchWriter::WriteHeader(uint8_t types[3],
204*a3a45f30SXin Li uint64_t ctrl_size,
205*a3a45f30SXin Li uint64_t diff_size) {
206*a3a45f30SXin Li /* Header format is
207*a3a45f30SXin Li * 0 8 magic header
208*a3a45f30SXin Li * 8 8 length of compressed ctrl block
209*a3a45f30SXin Li * 16 8 length of compressed diff block
210*a3a45f30SXin Li * 24 8 length of new file
211*a3a45f30SXin Li *
212*a3a45f30SXin Li * File format is
213*a3a45f30SXin Li * 0 32 Header
214*a3a45f30SXin Li * 32 ?? compressed ctrl block
215*a3a45f30SXin Li * ?? ?? compressed diff block
216*a3a45f30SXin Li * ?? ?? compressed extra block
217*a3a45f30SXin Li */
218*a3a45f30SXin Li uint8_t header[32];
219*a3a45f30SXin Li if (format_ == BsdiffFormat::kLegacy) {
220*a3a45f30SXin Li // The magic header is "BSDIFF40" for legacy format.
221*a3a45f30SXin Li memcpy(header, kLegacyMagicHeader, 8);
222*a3a45f30SXin Li } else if (format_ == BsdiffFormat::kBsdf2) {
223*a3a45f30SXin Li // The magic header for BSDF2 format:
224*a3a45f30SXin Li // 0 5 BSDF2
225*a3a45f30SXin Li // 5 1 compressed type for control stream
226*a3a45f30SXin Li // 6 1 compressed type for diff stream
227*a3a45f30SXin Li // 7 1 compressed type for extra stream
228*a3a45f30SXin Li memcpy(header, kBSDF2MagicHeader, 5);
229*a3a45f30SXin Li memcpy(header + 5, types, 3);
230*a3a45f30SXin Li } else {
231*a3a45f30SXin Li LOG(ERROR) << "Unsupported bsdiff format.";
232*a3a45f30SXin Li return false;
233*a3a45f30SXin Li }
234*a3a45f30SXin Li
235*a3a45f30SXin Li EncodeInt64(ctrl_size, header + 8);
236*a3a45f30SXin Li EncodeInt64(diff_size, header + 16);
237*a3a45f30SXin Li EncodeInt64(written_output_, header + 24);
238*a3a45f30SXin Li if (fwrite(header, sizeof(header), 1, fp_) != 1) {
239*a3a45f30SXin Li LOG(ERROR) << "writing to the patch file";
240*a3a45f30SXin Li return false;
241*a3a45f30SXin Li }
242*a3a45f30SXin Li return true;
243*a3a45f30SXin Li }
244*a3a45f30SXin Li
245*a3a45f30SXin Li } // namespace bsdiff
246