xref: /aosp_15_r20/external/puffin/src/puffpatch.cc (revision 07fb1d065b7cfb4729786fadd42a612532d2f466)
1*07fb1d06SElliott Hughes // Copyright 2017 The ChromiumOS Authors
2*07fb1d06SElliott Hughes // Use of this source code is governed by a BSD-style license that can be
3*07fb1d06SElliott Hughes // found in the LICENSE file.
4*07fb1d06SElliott Hughes 
5*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/puffpatch.h"
6*07fb1d06SElliott Hughes 
7*07fb1d06SElliott Hughes #include <endian.h>
8*07fb1d06SElliott Hughes #include <inttypes.h>
9*07fb1d06SElliott Hughes #include <unistd.h>
10*07fb1d06SElliott Hughes 
11*07fb1d06SElliott Hughes #include <algorithm>
12*07fb1d06SElliott Hughes #include <string>
13*07fb1d06SElliott Hughes #include <vector>
14*07fb1d06SElliott Hughes 
15*07fb1d06SElliott Hughes #include "bsdiff/bspatch.h"
16*07fb1d06SElliott Hughes #include "bsdiff/file_interface.h"
17*07fb1d06SElliott Hughes #include "zucchini/patch_reader.h"
18*07fb1d06SElliott Hughes #include "zucchini/zucchini.h"
19*07fb1d06SElliott Hughes 
20*07fb1d06SElliott Hughes #include "puffin/memory_stream.h"
21*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/brotli_util.h"
22*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/common.h"
23*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/huffer.h"
24*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/puffer.h"
25*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/stream.h"
26*07fb1d06SElliott Hughes #include "puffin/src/logging.h"
27*07fb1d06SElliott Hughes #include "puffin/src/puffin.pb.h"
28*07fb1d06SElliott Hughes #include "puffin/src/puffin_stream.h"
29*07fb1d06SElliott Hughes 
30*07fb1d06SElliott Hughes using std::string;
31*07fb1d06SElliott Hughes using std::unique_ptr;
32*07fb1d06SElliott Hughes using std::vector;
33*07fb1d06SElliott Hughes 
34*07fb1d06SElliott Hughes namespace puffin {
35*07fb1d06SElliott Hughes 
36*07fb1d06SElliott Hughes const char kMagic[] = "PUF1";
37*07fb1d06SElliott Hughes const size_t kMagicLength = 4;
38*07fb1d06SElliott Hughes 
39*07fb1d06SElliott Hughes namespace {
40*07fb1d06SElliott Hughes 
41*07fb1d06SElliott Hughes template <typename T>
CopyRpfToVector(const google::protobuf::RepeatedPtrField<metadata::BitExtent> & from,T * to,size_t coef)42*07fb1d06SElliott Hughes void CopyRpfToVector(
43*07fb1d06SElliott Hughes     const google::protobuf::RepeatedPtrField<metadata::BitExtent>& from,
44*07fb1d06SElliott Hughes     T* to,
45*07fb1d06SElliott Hughes     size_t coef) {
46*07fb1d06SElliott Hughes   to->reserve(from.size());
47*07fb1d06SElliott Hughes   for (const auto& ext : from) {
48*07fb1d06SElliott Hughes     to->emplace_back(ext.offset() / coef, ext.length() / coef);
49*07fb1d06SElliott Hughes   }
50*07fb1d06SElliott Hughes }
51*07fb1d06SElliott Hughes 
52*07fb1d06SElliott Hughes class BsdiffStream : public bsdiff::FileInterface {
53*07fb1d06SElliott Hughes  public:
54*07fb1d06SElliott Hughes   ~BsdiffStream() override = default;
55*07fb1d06SElliott Hughes 
Create(UniqueStreamPtr stream)56*07fb1d06SElliott Hughes   static unique_ptr<bsdiff::FileInterface> Create(UniqueStreamPtr stream) {
57*07fb1d06SElliott Hughes     TEST_AND_RETURN_VALUE(stream, nullptr);
58*07fb1d06SElliott Hughes     return unique_ptr<bsdiff::FileInterface>(
59*07fb1d06SElliott Hughes         new BsdiffStream(std::move(stream)));
60*07fb1d06SElliott Hughes   }
61*07fb1d06SElliott Hughes 
Read(void * buf,size_t count,size_t * bytes_read)62*07fb1d06SElliott Hughes   bool Read(void* buf, size_t count, size_t* bytes_read) override {
63*07fb1d06SElliott Hughes     *bytes_read = 0;
64*07fb1d06SElliott Hughes     if (stream_->Read(buf, count)) {
65*07fb1d06SElliott Hughes       *bytes_read = count;
66*07fb1d06SElliott Hughes       return true;
67*07fb1d06SElliott Hughes     }
68*07fb1d06SElliott Hughes     return false;
69*07fb1d06SElliott Hughes   }
70*07fb1d06SElliott Hughes 
Write(const void * buf,size_t count,size_t * bytes_written)71*07fb1d06SElliott Hughes   bool Write(const void* buf, size_t count, size_t* bytes_written) override {
72*07fb1d06SElliott Hughes     *bytes_written = 0;
73*07fb1d06SElliott Hughes     if (stream_->Write(buf, count)) {
74*07fb1d06SElliott Hughes       *bytes_written = count;
75*07fb1d06SElliott Hughes       return true;
76*07fb1d06SElliott Hughes     }
77*07fb1d06SElliott Hughes     return false;
78*07fb1d06SElliott Hughes   }
79*07fb1d06SElliott Hughes 
Seek(off_t pos)80*07fb1d06SElliott Hughes   bool Seek(off_t pos) override { return stream_->Seek(pos); }
81*07fb1d06SElliott Hughes 
Close()82*07fb1d06SElliott Hughes   bool Close() override { return stream_->Close(); }
83*07fb1d06SElliott Hughes 
GetSize(uint64_t * size)84*07fb1d06SElliott Hughes   bool GetSize(uint64_t* size) override {
85*07fb1d06SElliott Hughes     uint64_t my_size;
86*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(stream_->GetSize(&my_size));
87*07fb1d06SElliott Hughes     *size = my_size;
88*07fb1d06SElliott Hughes     return true;
89*07fb1d06SElliott Hughes   }
90*07fb1d06SElliott Hughes 
91*07fb1d06SElliott Hughes  private:
BsdiffStream(UniqueStreamPtr stream)92*07fb1d06SElliott Hughes   explicit BsdiffStream(UniqueStreamPtr stream) : stream_(std::move(stream)) {}
93*07fb1d06SElliott Hughes 
94*07fb1d06SElliott Hughes   UniqueStreamPtr stream_;
95*07fb1d06SElliott Hughes 
96*07fb1d06SElliott Hughes   DISALLOW_COPY_AND_ASSIGN(BsdiffStream);
97*07fb1d06SElliott Hughes };
98*07fb1d06SElliott Hughes 
DecodePatch(const uint8_t * patch,size_t patch_length,size_t * bsdiff_patch_offset,size_t * bsdiff_patch_size,vector<BitExtent> * src_deflates,vector<BitExtent> * dst_deflates,vector<ByteExtent> * src_puffs,vector<ByteExtent> * dst_puffs,uint64_t * src_puff_size,uint64_t * dst_puff_size,metadata::PatchHeader_PatchType * patch_type)99*07fb1d06SElliott Hughes bool DecodePatch(const uint8_t* patch,
100*07fb1d06SElliott Hughes                  size_t patch_length,
101*07fb1d06SElliott Hughes                  size_t* bsdiff_patch_offset,
102*07fb1d06SElliott Hughes                  size_t* bsdiff_patch_size,
103*07fb1d06SElliott Hughes                  vector<BitExtent>* src_deflates,
104*07fb1d06SElliott Hughes                  vector<BitExtent>* dst_deflates,
105*07fb1d06SElliott Hughes                  vector<ByteExtent>* src_puffs,
106*07fb1d06SElliott Hughes                  vector<ByteExtent>* dst_puffs,
107*07fb1d06SElliott Hughes                  uint64_t* src_puff_size,
108*07fb1d06SElliott Hughes                  uint64_t* dst_puff_size,
109*07fb1d06SElliott Hughes                  metadata::PatchHeader_PatchType* patch_type) {
110*07fb1d06SElliott Hughes   size_t offset = 0;
111*07fb1d06SElliott Hughes   uint32_t header_size;
112*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(patch_length >= (kMagicLength + sizeof(header_size)));
113*07fb1d06SElliott Hughes 
114*07fb1d06SElliott Hughes   string patch_magic(reinterpret_cast<const char*>(patch), kMagicLength);
115*07fb1d06SElliott Hughes   if (patch_magic != kMagic) {
116*07fb1d06SElliott Hughes     LOG(ERROR) << "Magic number for Puffin patch is incorrect: " << patch_magic;
117*07fb1d06SElliott Hughes     return false;
118*07fb1d06SElliott Hughes   }
119*07fb1d06SElliott Hughes   offset += kMagicLength;
120*07fb1d06SElliott Hughes 
121*07fb1d06SElliott Hughes   // Read the header size from big-endian mode.
122*07fb1d06SElliott Hughes   memcpy(&header_size, patch + offset, sizeof(header_size));
123*07fb1d06SElliott Hughes   header_size = be32toh(header_size);
124*07fb1d06SElliott Hughes   offset += sizeof(header_size);
125*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(header_size <= (patch_length - offset));
126*07fb1d06SElliott Hughes 
127*07fb1d06SElliott Hughes   metadata::PatchHeader header;
128*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(header.ParseFromArray(patch + offset, header_size));
129*07fb1d06SElliott Hughes   offset += header_size;
130*07fb1d06SElliott Hughes 
131*07fb1d06SElliott Hughes   CopyRpfToVector(header.src().deflates(), src_deflates, 1);
132*07fb1d06SElliott Hughes   CopyRpfToVector(header.dst().deflates(), dst_deflates, 1);
133*07fb1d06SElliott Hughes   CopyRpfToVector(header.src().puffs(), src_puffs, 8);
134*07fb1d06SElliott Hughes   CopyRpfToVector(header.dst().puffs(), dst_puffs, 8);
135*07fb1d06SElliott Hughes 
136*07fb1d06SElliott Hughes   *src_puff_size = header.src().puff_length();
137*07fb1d06SElliott Hughes   *dst_puff_size = header.dst().puff_length();
138*07fb1d06SElliott Hughes 
139*07fb1d06SElliott Hughes   *bsdiff_patch_offset = offset;
140*07fb1d06SElliott Hughes   *bsdiff_patch_size = patch_length - offset;
141*07fb1d06SElliott Hughes 
142*07fb1d06SElliott Hughes   *patch_type = header.type();
143*07fb1d06SElliott Hughes   return true;
144*07fb1d06SElliott Hughes }
145*07fb1d06SElliott Hughes 
ApplyZucchiniPatch(UniqueStreamPtr src_stream,size_t src_size,const uint8_t * patch_start,size_t patch_size,UniqueStreamPtr dst_stream)146*07fb1d06SElliott Hughes bool ApplyZucchiniPatch(UniqueStreamPtr src_stream,
147*07fb1d06SElliott Hughes                         size_t src_size,
148*07fb1d06SElliott Hughes                         const uint8_t* patch_start,
149*07fb1d06SElliott Hughes                         size_t patch_size,
150*07fb1d06SElliott Hughes                         UniqueStreamPtr dst_stream) {
151*07fb1d06SElliott Hughes   // Read the source data
152*07fb1d06SElliott Hughes   Buffer puffed_src(src_size);
153*07fb1d06SElliott Hughes   Buffer buffer(1024 * 1024);
154*07fb1d06SElliott Hughes   uint64_t bytes_wrote = 0;
155*07fb1d06SElliott Hughes   while (bytes_wrote < src_size) {
156*07fb1d06SElliott Hughes     auto write_size =
157*07fb1d06SElliott Hughes         std::min(static_cast<uint64_t>(buffer.size()), src_size - bytes_wrote);
158*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(src_stream->Read(buffer.data(), write_size));
159*07fb1d06SElliott Hughes     std::copy(buffer.data(), buffer.data() + write_size,
160*07fb1d06SElliott Hughes               puffed_src.data() + bytes_wrote);
161*07fb1d06SElliott Hughes     bytes_wrote += write_size;
162*07fb1d06SElliott Hughes   }
163*07fb1d06SElliott Hughes   // Read the patch
164*07fb1d06SElliott Hughes   Buffer zucchini_patch;
165*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(BrotliDecode(patch_start, patch_size, &zucchini_patch));
166*07fb1d06SElliott Hughes   auto patch_reader = zucchini::EnsemblePatchReader::Create(
167*07fb1d06SElliott Hughes       {zucchini_patch.data(), zucchini_patch.size()});
168*07fb1d06SElliott Hughes   if (!patch_reader.has_value()) {
169*07fb1d06SElliott Hughes     LOG(ERROR) << "Failed to parse the zucchini patch.";
170*07fb1d06SElliott Hughes     return false;
171*07fb1d06SElliott Hughes   }
172*07fb1d06SElliott Hughes 
173*07fb1d06SElliott Hughes   // TODO(197361113) Stream the patched result once zucchini supports it. So we
174*07fb1d06SElliott Hughes   // can save some memory when applying patch on device.
175*07fb1d06SElliott Hughes   Buffer patched_data(patch_reader->header().new_size);
176*07fb1d06SElliott Hughes   auto status = zucchini::ApplyBuffer(
177*07fb1d06SElliott Hughes       {puffed_src.data(), puffed_src.size()}, *patch_reader,
178*07fb1d06SElliott Hughes       {patched_data.data(), patched_data.size()});
179*07fb1d06SElliott Hughes   if (status != zucchini::status::kStatusSuccess) {
180*07fb1d06SElliott Hughes     LOG(ERROR) << "Failed to parse the zucchini patch: " << status;
181*07fb1d06SElliott Hughes     return false;
182*07fb1d06SElliott Hughes   }
183*07fb1d06SElliott Hughes 
184*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(
185*07fb1d06SElliott Hughes       dst_stream->Write(patched_data.data(), patched_data.size()));
186*07fb1d06SElliott Hughes   return true;
187*07fb1d06SElliott Hughes }
188*07fb1d06SElliott Hughes 
189*07fb1d06SElliott Hughes }  // namespace
190*07fb1d06SElliott Hughes 
PuffPatch(UniqueStreamPtr src,UniqueStreamPtr dst,const uint8_t * patch,size_t patch_length,size_t max_cache_size)191*07fb1d06SElliott Hughes bool PuffPatch(UniqueStreamPtr src,
192*07fb1d06SElliott Hughes                UniqueStreamPtr dst,
193*07fb1d06SElliott Hughes                const uint8_t* patch,
194*07fb1d06SElliott Hughes                size_t patch_length,
195*07fb1d06SElliott Hughes                size_t max_cache_size) {
196*07fb1d06SElliott Hughes   size_t patch_offset;  // raw patch offset in puffin |patch|.
197*07fb1d06SElliott Hughes   size_t raw_patch_size = 0;
198*07fb1d06SElliott Hughes   vector<BitExtent> src_deflates, dst_deflates;
199*07fb1d06SElliott Hughes   vector<ByteExtent> src_puffs, dst_puffs;
200*07fb1d06SElliott Hughes   uint64_t src_puff_size, dst_puff_size;
201*07fb1d06SElliott Hughes 
202*07fb1d06SElliott Hughes   metadata::PatchHeader_PatchType patch_type;
203*07fb1d06SElliott Hughes 
204*07fb1d06SElliott Hughes   // Decode the patch and get the raw patch (e.g. bsdiff, zucchini).
205*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(
206*07fb1d06SElliott Hughes       DecodePatch(patch, patch_length, &patch_offset, &raw_patch_size,
207*07fb1d06SElliott Hughes                   &src_deflates, &dst_deflates, &src_puffs, &dst_puffs,
208*07fb1d06SElliott Hughes                   &src_puff_size, &dst_puff_size, &patch_type));
209*07fb1d06SElliott Hughes   auto puffer = std::make_shared<Puffer>();
210*07fb1d06SElliott Hughes   auto huffer = std::make_shared<Huffer>();
211*07fb1d06SElliott Hughes 
212*07fb1d06SElliott Hughes   auto src_stream =
213*07fb1d06SElliott Hughes       PuffinStream::CreateForPuff(std::move(src), puffer, src_puff_size,
214*07fb1d06SElliott Hughes                                   src_deflates, src_puffs, max_cache_size);
215*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(src_stream);
216*07fb1d06SElliott Hughes   auto dst_stream = PuffinStream::CreateForHuff(
217*07fb1d06SElliott Hughes       std::move(dst), huffer, dst_puff_size, dst_deflates, dst_puffs);
218*07fb1d06SElliott Hughes   TEST_AND_RETURN_FALSE(dst_stream);
219*07fb1d06SElliott Hughes 
220*07fb1d06SElliott Hughes   if (patch_type == metadata::PatchHeader_PatchType_BSDIFF) {
221*07fb1d06SElliott Hughes     // For reading from source.
222*07fb1d06SElliott Hughes     auto reader = BsdiffStream::Create(std::move(src_stream));
223*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(reader);
224*07fb1d06SElliott Hughes     // For writing into destination.
225*07fb1d06SElliott Hughes     auto writer = BsdiffStream::Create(std::move(dst_stream));
226*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(writer);
227*07fb1d06SElliott Hughes 
228*07fb1d06SElliott Hughes     // Running bspatch itself.
229*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(
230*07fb1d06SElliott Hughes         0 == bspatch(reader, writer, &patch[patch_offset], raw_patch_size));
231*07fb1d06SElliott Hughes   } else if (patch_type == metadata::PatchHeader_PatchType_ZUCCHINI) {
232*07fb1d06SElliott Hughes     TEST_AND_RETURN_FALSE(ApplyZucchiniPatch(
233*07fb1d06SElliott Hughes         std::move(src_stream), src_puff_size, patch + patch_offset,
234*07fb1d06SElliott Hughes         raw_patch_size, std::move(dst_stream)));
235*07fb1d06SElliott Hughes   } else {
236*07fb1d06SElliott Hughes     LOG(ERROR) << "Unsupported patch type " << patch_type;
237*07fb1d06SElliott Hughes     return false;
238*07fb1d06SElliott Hughes   }
239*07fb1d06SElliott Hughes   return true;
240*07fb1d06SElliott Hughes }
241*07fb1d06SElliott Hughes 
242*07fb1d06SElliott Hughes }  // namespace puffin
243