xref: /aosp_15_r20/external/bsdiff/patch_writer.cc (revision a3a45f308bd90ef1a6e6a5e8fb92fe449b895909)
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