Home | History | Annotate | Download | only in src
      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>
     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 // +-------+------------------+-------------+--------------+
     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 
     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 
    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