Home | History | Annotate | Download | only in bsdiff
      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 "bsdiff/patch_reader.h"
      6 
      7 #include <string.h>
      8 
      9 #include <limits>
     10 
     11 #include "bsdiff/brotli_decompressor.h"
     12 #include "bsdiff/bspatch.h"
     13 #include "bsdiff/bz2_decompressor.h"
     14 #include "bsdiff/constants.h"
     15 #include "bsdiff/logging.h"
     16 #include "bsdiff/utils.h"
     17 
     18 namespace bsdiff {
     19 
     20 bool BsdiffPatchReader::Init(const uint8_t* patch_data, size_t patch_size) {
     21   //   File format:
     22   //   0       8    magic header
     23   //   8       8    X
     24   //   16      8    Y
     25   //   24      8    new_file_size
     26   //   32      X    compressed control block
     27   //   32+X    Y    compressed diff block
     28   //   32+X+Y  ???  compressed extra block
     29   // with control block a set of triples (x,y,z) meaning "add x bytes
     30   // from oldfile to x bytes from the diff block; copy y bytes from the
     31   // extra block; seek forwards in oldfile by z bytes".
     32 
     33   if (patch_size < 32) {
     34     LOG(ERROR) << "Too small to be a bspatch.";
     35     return false;
     36   }
     37   // Check for appropriate magic.
     38   std::vector<CompressorType> compression_type;
     39   if (memcmp(patch_data, kLegacyMagicHeader, 8) == 0) {
     40     // The magic header is "BSDIFF40" for legacy format.
     41     compression_type = {CompressorType::kBZ2, CompressorType::kBZ2,
     42                         CompressorType::kBZ2};
     43   } else if (memcmp(patch_data, kBSDF2MagicHeader, 5) == 0) {
     44     // The magic header for BSDF2 format:
     45     // 0 5 BSDF2
     46     // 5 1 compressed type for control stream
     47     // 6 1 compressed type for diff stream
     48     // 7 1 compressed type for extra stream
     49     for (size_t i = 5; i < 8; i++) {
     50       uint8_t type = patch_data[i];
     51       switch (type) {
     52         case static_cast<uint8_t>(CompressorType::kBZ2):
     53           compression_type.push_back(CompressorType::kBZ2);
     54           break;
     55         case static_cast<uint8_t>(CompressorType::kBrotli):
     56           compression_type.push_back(CompressorType::kBrotli);
     57           break;
     58         default:
     59           LOG(ERROR) << "Unsupported compression type: "
     60                      << static_cast<int>(type);
     61           return false;
     62       }
     63     }
     64   } else {
     65     LOG(ERROR) << "Not a bsdiff patch.";
     66     return false;
     67   }
     68 
     69   // Read lengths from header.
     70   int64_t ctrl_len = ParseInt64(patch_data + 8);
     71   int64_t diff_len = ParseInt64(patch_data + 16);
     72   int64_t signed_newsize = ParseInt64(patch_data + 24);
     73   if ((ctrl_len < 0) || (diff_len < 0) || (signed_newsize < 0) ||
     74       (32 + ctrl_len + diff_len > static_cast<int64_t>(patch_size))) {
     75     LOG(ERROR) << "Corrupt patch.  ctrl_len: " << ctrl_len
     76                << ", data_len: " << diff_len
     77                << ", new_file_size: " << signed_newsize
     78                << ", patch_size: " << patch_size;
     79     return false;
     80   }
     81   new_file_size_ = signed_newsize;
     82 
     83   ctrl_stream_ = CreateDecompressor(compression_type[0]);
     84   diff_stream_ = CreateDecompressor(compression_type[1]);
     85   extra_stream_ = CreateDecompressor(compression_type[2]);
     86   if (!(ctrl_stream_ && diff_stream_ && extra_stream_)) {
     87     LOG(ERROR) << "uninitialized decompressor stream";
     88     return false;
     89   }
     90 
     91   size_t offset = 32;
     92   if (!ctrl_stream_->SetInputData(const_cast<uint8_t*>(patch_data) + offset,
     93                                   ctrl_len)) {
     94     LOG(ERROR) << "Failed to init ctrl stream, ctrl_len: " << ctrl_len;
     95     return false;
     96   }
     97 
     98   offset += ctrl_len;
     99   if (!diff_stream_->SetInputData(const_cast<uint8_t*>(patch_data) + offset,
    100                                   diff_len)) {
    101     LOG(ERROR) << "Failed to init ctrl stream, diff_len: " << diff_len;
    102     return false;
    103   }
    104 
    105   offset += diff_len;
    106   if (!extra_stream_->SetInputData(const_cast<uint8_t*>(patch_data) + offset,
    107                                    patch_size - offset)) {
    108     LOG(ERROR) << "Failed to init extra stream, extra_offset: " << offset
    109                << ", patch_size: " << patch_size;
    110     return false;
    111   }
    112   return true;
    113 }
    114 
    115 bool BsdiffPatchReader::ParseControlEntry(ControlEntry* control_entry) {
    116   if (!control_entry)
    117     return false;
    118 
    119   uint8_t buf[8];
    120   if (!ctrl_stream_->Read(buf, 8))
    121     return false;
    122   int64_t diff_size = ParseInt64(buf);
    123 
    124   if (!ctrl_stream_->Read(buf, 8))
    125     return false;
    126   int64_t extra_size = ParseInt64(buf);
    127 
    128   // Sanity check.
    129   if (diff_size < 0 || extra_size < 0) {
    130     LOG(ERROR) << "Corrupt patch; diff_size: " << diff_size
    131                << ", extra_size: " << extra_size;
    132     return false;
    133   }
    134 
    135   control_entry->diff_size = diff_size;
    136   control_entry->extra_size = extra_size;
    137 
    138   if (!ctrl_stream_->Read(buf, 8))
    139     return false;
    140   control_entry->offset_increment = ParseInt64(buf);
    141 
    142   return true;
    143 }
    144 
    145 bool BsdiffPatchReader::ReadDiffStream(uint8_t* buf, size_t size) {
    146   return diff_stream_->Read(buf, size);
    147 }
    148 
    149 bool BsdiffPatchReader::ReadExtraStream(uint8_t* buf, size_t size) {
    150   return extra_stream_->Read(buf, size);
    151 }
    152 
    153 bool BsdiffPatchReader::Finish() {
    154   if (!ctrl_stream_->Close()) {
    155     LOG(ERROR) << "Failed to close the control stream";
    156     return false;
    157   }
    158 
    159   if (!diff_stream_->Close()) {
    160     LOG(ERROR) << "Failed to close the diff stream";
    161     return false;
    162   }
    163 
    164   if (!extra_stream_->Close()) {
    165     LOG(ERROR) << "Failed to close the extra stream";
    166     return false;
    167   }
    168   return true;
    169 }
    170 
    171 }  // namespace bsdiff
    172