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