• 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 "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 
Init(const uint8_t * patch_data,size_t patch_size)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 
ParseControlEntry(ControlEntry * control_entry)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 
ReadDiffStream(uint8_t * buf,size_t size)145 bool BsdiffPatchReader::ReadDiffStream(uint8_t* buf, size_t size) {
146   return diff_stream_->Read(buf, size);
147 }
148 
ReadExtraStream(uint8_t * buf,size_t size)149 bool BsdiffPatchReader::ReadExtraStream(uint8_t* buf, size_t size) {
150   return extra_stream_->Read(buf, size);
151 }
152 
Finish()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