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/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>
     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 
     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:
     93   explicit BsdiffStream(UniqueStreamPtr stream) : stream_(std::move(stream)) {}
     94   ~BsdiffStream() override = default;
     95 
     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 
    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 
    114   bool Seek(off_t pos) override { return stream_->Seek(pos); }
    115 
    116   bool Close() override { return stream_->Close(); }
    117 
    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 
    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