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