• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/puffdiff.h"
6 
7 #include <endian.h>
8 #include <inttypes.h>
9 #include <unistd.h>
10 
11 #include <string>
12 #include <vector>
13 
14 #include "bsdiff/bsdiff.h"
15 
16 #include "puffin/src/include/puffin/common.h"
17 #include "puffin/src/include/puffin/puffer.h"
18 #include "puffin/src/include/puffin/puffpatch.h"
19 #include "puffin/src/include/puffin/utils.h"
20 #include "puffin/src/file_stream.h"
21 #include "puffin/src/memory_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 namespace {
32 
33 template <typename T>
CopyVectorToRpf(const T & from,google::protobuf::RepeatedPtrField<metadata::BitExtent> * to,size_t coef)34 void CopyVectorToRpf(
35     const T& from,
36     google::protobuf::RepeatedPtrField<metadata::BitExtent>* to,
37     size_t coef) {
38   to->Reserve(from.size());
39   for (const auto& ext : from) {
40     auto tmp = to->Add();
41     tmp->set_offset(ext.offset * coef);
42     tmp->set_length(ext.length * coef);
43   }
44 }
45 
46 // Structure of a puffin patch
47 // +-------+------------------+-------------+--------------+
48 // |P|U|F|1| PatchHeader Size | PatchHeader | bsdiff_patch |
49 // +-------+------------------+-------------+--------------+
CreatePatch(const Buffer & bsdiff_patch,const vector<BitExtent> & src_deflates,const vector<BitExtent> & dst_deflates,const vector<ByteExtent> & src_puffs,const vector<ByteExtent> & dst_puffs,uint64_t src_puff_size,uint64_t dst_puff_size,Buffer * patch)50 bool CreatePatch(const Buffer& bsdiff_patch,
51                  const vector<BitExtent>& src_deflates,
52                  const vector<BitExtent>& dst_deflates,
53                  const vector<ByteExtent>& src_puffs,
54                  const vector<ByteExtent>& dst_puffs,
55                  uint64_t src_puff_size,
56                  uint64_t dst_puff_size,
57                  Buffer* patch) {
58   metadata::PatchHeader header;
59   header.set_version(1);
60 
61   CopyVectorToRpf(src_deflates, header.mutable_src()->mutable_deflates(), 1);
62   CopyVectorToRpf(dst_deflates, header.mutable_dst()->mutable_deflates(), 1);
63   CopyVectorToRpf(src_puffs, header.mutable_src()->mutable_puffs(), 8);
64   CopyVectorToRpf(dst_puffs, header.mutable_dst()->mutable_puffs(), 8);
65 
66   header.mutable_src()->set_puff_length(src_puff_size);
67   header.mutable_dst()->set_puff_length(dst_puff_size);
68 
69   const uint32_t header_size = header.ByteSize();
70 
71   uint64_t offset = 0;
72   patch->resize(kMagicLength + sizeof(header_size) + header_size +
73                 bsdiff_patch.size());
74 
75   memcpy(patch->data() + offset, kMagic, kMagicLength);
76   offset += kMagicLength;
77 
78   // Read header size from big-endian mode.
79   uint32_t be_header_size = htobe32(header_size);
80   memcpy(patch->data() + offset, &be_header_size, sizeof(be_header_size));
81   offset += 4;
82 
83   TEST_AND_RETURN_FALSE(
84       header.SerializeToArray(patch->data() + offset, header_size));
85   offset += header_size;
86 
87   memcpy(patch->data() + offset, bsdiff_patch.data(), bsdiff_patch.size());
88 
89   if (bsdiff_patch.size() > patch->size()) {
90     LOG(ERROR) << "Puffin patch is invalid";
91   }
92   return true;
93 }
94 
95 }  // namespace
96 
PuffDiff(UniqueStreamPtr src,UniqueStreamPtr dst,const vector<BitExtent> & src_deflates,const vector<BitExtent> & dst_deflates,const string & tmp_filepath,Buffer * patch)97 bool PuffDiff(UniqueStreamPtr src,
98               UniqueStreamPtr dst,
99               const vector<BitExtent>& src_deflates,
100               const vector<BitExtent>& dst_deflates,
101               const string& tmp_filepath,
102               Buffer* patch) {
103   auto puffer = std::make_shared<Puffer>();
104   auto puff_deflate_stream =
105       [&puffer](UniqueStreamPtr stream, const vector<BitExtent>& deflates,
106                 Buffer* puff_buffer, vector<ByteExtent>* puffs) {
107         uint64_t puff_size;
108         TEST_AND_RETURN_FALSE(stream->Seek(0));
109         TEST_AND_RETURN_FALSE(
110             FindPuffLocations(stream, deflates, puffs, &puff_size));
111         TEST_AND_RETURN_FALSE(stream->Seek(0));
112         auto src_puffin_stream = PuffinStream::CreateForPuff(
113             std::move(stream), puffer, puff_size, deflates, *puffs);
114         puff_buffer->resize(puff_size);
115         TEST_AND_RETURN_FALSE(
116             src_puffin_stream->Read(puff_buffer->data(), puff_buffer->size()));
117         return true;
118       };
119 
120   Buffer src_puff_buffer;
121   Buffer dst_puff_buffer;
122   vector<ByteExtent> src_puffs, dst_puffs;
123   TEST_AND_RETURN_FALSE(puff_deflate_stream(std::move(src), src_deflates,
124                                             &src_puff_buffer, &src_puffs));
125   TEST_AND_RETURN_FALSE(puff_deflate_stream(std::move(dst), dst_deflates,
126                                             &dst_puff_buffer, &dst_puffs));
127 
128   TEST_AND_RETURN_FALSE(
129       0 == bsdiff::bsdiff(src_puff_buffer.data(), src_puff_buffer.size(),
130                           dst_puff_buffer.data(), dst_puff_buffer.size(),
131                           tmp_filepath.c_str(), nullptr));
132 
133   auto bsdiff_patch = FileStream::Open(tmp_filepath, true, false);
134   TEST_AND_RETURN_FALSE(bsdiff_patch);
135   uint64_t patch_size;
136   TEST_AND_RETURN_FALSE(bsdiff_patch->GetSize(&patch_size));
137   Buffer bsdiff_patch_buf(patch_size);
138   TEST_AND_RETURN_FALSE(
139       bsdiff_patch->Read(bsdiff_patch_buf.data(), bsdiff_patch_buf.size()));
140   TEST_AND_RETURN_FALSE(bsdiff_patch->Close());
141 
142   TEST_AND_RETURN_FALSE(CreatePatch(
143       bsdiff_patch_buf, src_deflates, dst_deflates, src_puffs, dst_puffs,
144       src_puff_buffer.size(), dst_puff_buffer.size(), patch));
145   return true;
146 }
147 
PuffDiff(const Buffer & src,const Buffer & dst,const vector<BitExtent> & src_deflates,const vector<BitExtent> & dst_deflates,const string & tmp_filepath,Buffer * patch)148 bool PuffDiff(const Buffer& src,
149               const Buffer& dst,
150               const vector<BitExtent>& src_deflates,
151               const vector<BitExtent>& dst_deflates,
152               const string& tmp_filepath,
153               Buffer* patch) {
154   return PuffDiff(MemoryStream::CreateForRead(src),
155                   MemoryStream::CreateForRead(dst), src_deflates, dst_deflates,
156                   tmp_filepath, patch);
157 }
158 
159 }  // namespace puffin
160