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