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/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 
18 #include "puffin/src/include/puffin/common.h"
19 #include "puffin/src/include/puffin/huffer.h"
20 #include "puffin/src/include/puffin/puffer.h"
21 #include "puffin/src/include/puffin/stream.h"
22 #include "puffin/src/logging.h"
23 #include "puffin/src/puffin.pb.h"
24 #include "puffin/src/puffin_stream.h"
25 
26 using std::string;
27 using std::unique_ptr;
28 using std::vector;
29 
30 namespace puffin {
31 
32 const char kMagic[] = "PUF1";
33 const size_t kMagicLength = 4;
34 
35 namespace {
36 
37 template <typename T>
CopyRpfToVector(const google::protobuf::RepeatedPtrField<metadata::BitExtent> & from,T * to,size_t coef)38 void CopyRpfToVector(
39     const google::protobuf::RepeatedPtrField<metadata::BitExtent>& from,
40     T* to,
41     size_t coef) {
42   to->reserve(from.size());
43   for (const auto& ext : from) {
44     to->emplace_back(ext.offset() / coef, ext.length() / coef);
45   }
46 }
47 
48 class BsdiffStream : public bsdiff::FileInterface {
49  public:
50   ~BsdiffStream() override = default;
51 
Create(UniqueStreamPtr stream)52   static unique_ptr<bsdiff::FileInterface> Create(UniqueStreamPtr stream) {
53     TEST_AND_RETURN_VALUE(stream, nullptr);
54     return unique_ptr<bsdiff::FileInterface>(
55         new BsdiffStream(std::move(stream)));
56   }
57 
Read(void * buf,size_t count,size_t * bytes_read)58   bool Read(void* buf, size_t count, size_t* bytes_read) override {
59     *bytes_read = 0;
60     if (stream_->Read(buf, count)) {
61       *bytes_read = count;
62       return true;
63     }
64     return false;
65   }
66 
Write(const void * buf,size_t count,size_t * bytes_written)67   bool Write(const void* buf, size_t count, size_t* bytes_written) override {
68     *bytes_written = 0;
69     if (stream_->Write(buf, count)) {
70       *bytes_written = count;
71       return true;
72     }
73     return false;
74   }
75 
Seek(off_t pos)76   bool Seek(off_t pos) override { return stream_->Seek(pos); }
77 
Close()78   bool Close() override { return stream_->Close(); }
79 
GetSize(uint64_t * size)80   bool GetSize(uint64_t* size) override {
81     uint64_t my_size;
82     TEST_AND_RETURN_FALSE(stream_->GetSize(&my_size));
83     *size = my_size;
84     return true;
85   }
86 
87  private:
BsdiffStream(UniqueStreamPtr stream)88   explicit BsdiffStream(UniqueStreamPtr stream) : stream_(std::move(stream)) {}
89 
90   UniqueStreamPtr stream_;
91 
92   DISALLOW_COPY_AND_ASSIGN(BsdiffStream);
93 };
94 
95 }  // namespace
96 
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)97 bool DecodePatch(const uint8_t* patch,
98                  size_t patch_length,
99                  size_t* bsdiff_patch_offset,
100                  size_t* bsdiff_patch_size,
101                  vector<BitExtent>* src_deflates,
102                  vector<BitExtent>* dst_deflates,
103                  vector<ByteExtent>* src_puffs,
104                  vector<ByteExtent>* dst_puffs,
105                  uint64_t* src_puff_size,
106                  uint64_t* dst_puff_size) {
107   size_t offset = 0;
108   uint32_t header_size;
109   TEST_AND_RETURN_FALSE(patch_length >= (kMagicLength + sizeof(header_size)));
110 
111   string patch_magic(reinterpret_cast<const char*>(patch), kMagicLength);
112   if (patch_magic != kMagic) {
113     LOG(ERROR) << "Magic number for Puffin patch is incorrect: " << patch_magic;
114     return false;
115   }
116   offset += kMagicLength;
117 
118   // Read the header size from big-endian mode.
119   memcpy(&header_size, patch + offset, sizeof(header_size));
120   header_size = be32toh(header_size);
121   offset += sizeof(header_size);
122   TEST_AND_RETURN_FALSE(header_size <= (patch_length - offset));
123 
124   metadata::PatchHeader header;
125   TEST_AND_RETURN_FALSE(header.ParseFromArray(patch + offset, header_size));
126   offset += header_size;
127 
128   CopyRpfToVector(header.src().deflates(), src_deflates, 1);
129   CopyRpfToVector(header.dst().deflates(), dst_deflates, 1);
130   CopyRpfToVector(header.src().puffs(), src_puffs, 8);
131   CopyRpfToVector(header.dst().puffs(), dst_puffs, 8);
132 
133   *src_puff_size = header.src().puff_length();
134   *dst_puff_size = header.dst().puff_length();
135 
136   *bsdiff_patch_offset = offset;
137   *bsdiff_patch_size = patch_length - offset;
138   return true;
139 }
140 
PuffPatch(UniqueStreamPtr src,UniqueStreamPtr dst,const uint8_t * patch,size_t patch_length,size_t max_cache_size)141 bool PuffPatch(UniqueStreamPtr src,
142                UniqueStreamPtr dst,
143                const uint8_t* patch,
144                size_t patch_length,
145                size_t max_cache_size) {
146   size_t bsdiff_patch_offset;  // bsdiff offset in |patch|.
147   size_t bsdiff_patch_size = 0;
148   vector<BitExtent> src_deflates, dst_deflates;
149   vector<ByteExtent> src_puffs, dst_puffs;
150   uint64_t src_puff_size, dst_puff_size;
151 
152   // Decode the patch and get the bsdiff_patch.
153   TEST_AND_RETURN_FALSE(DecodePatch(patch, patch_length, &bsdiff_patch_offset,
154                                     &bsdiff_patch_size, &src_deflates,
155                                     &dst_deflates, &src_puffs, &dst_puffs,
156                                     &src_puff_size, &dst_puff_size));
157   auto puffer = std::make_shared<Puffer>();
158   auto huffer = std::make_shared<Huffer>();
159 
160   // For reading from source.
161   auto reader = BsdiffStream::Create(
162       PuffinStream::CreateForPuff(std::move(src), puffer, src_puff_size,
163                                   src_deflates, src_puffs, max_cache_size));
164   TEST_AND_RETURN_FALSE(reader);
165 
166   // For writing into destination.
167   auto writer = BsdiffStream::Create(PuffinStream::CreateForHuff(
168       std::move(dst), huffer, dst_puff_size, dst_deflates, dst_puffs));
169   TEST_AND_RETURN_FALSE(writer);
170 
171   // Running bspatch itself.
172   TEST_AND_RETURN_FALSE(0 == bspatch(reader, writer,
173                                      &patch[bsdiff_patch_offset],
174                                      bsdiff_patch_size));
175   return true;
176 }
177 
178 }  // namespace puffin
179