blob: ee9e9c460c7af94b5c2576be18d6c120ebe943ca [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
11#include "bsdiff/bspatch.h"
12#include "bsdiff/bz2_decompressor.h"
13#include "bsdiff/logging.h"
14#include "bsdiff/utils.h"
15
16using std::endl;
17
18namespace bsdiff {
19
20const uint8_t kMagicHeader[] = "BSDIFF40";
21
22bool BsdiffPatchReader::Init(const uint8_t* patch_data, size_t patch_size) {
23 // File format:
24 // 0 8 "BSDIFF40"
25 // 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
35 // Check for appropriate magic.
36 if (memcmp(patch_data, kMagicHeader, 8) != 0) {
37 LOG(ERROR) << "Not a bsdiff patch." << endl;
38 return false;
39 }
40
41 // Read lengths from header.
42 int64_t ctrl_len = ParseInt64(patch_data + 8);
43 int64_t diff_len = ParseInt64(patch_data + 16);
44 int64_t signed_newsize = ParseInt64(patch_data + 24);
45 if ((ctrl_len < 0) || (diff_len < 0) || (signed_newsize < 0) ||
46 (32 + ctrl_len + diff_len > static_cast<int64_t>(patch_size))) {
47 LOG(ERROR) << "Corrupt patch. ctrl_len: " << ctrl_len
48 << ", data_len: " << diff_len
49 << ", new_file_size: " << signed_newsize
50 << ", patch_size: " << patch_size;
51 return false;
52 }
53 new_file_size_ = signed_newsize;
54
55 // TODO(xunchang) set the correct decompressor based on the info in the
56 // header.
57 ctrl_stream_.reset(new BZ2Decompressor());
58 diff_stream_.reset(new BZ2Decompressor());
59 extra_stream_.reset(new BZ2Decompressor());
60
61 int64_t offset = 32;
62 if (!ctrl_stream_->SetInputData(const_cast<uint8_t*>(patch_data) + offset,
63 ctrl_len)) {
64 LOG(ERROR) << "Failed to init ctrl stream, ctrl_len: " << ctrl_len << endl;
65 return false;
66 }
67
68 offset += ctrl_len;
69 if (!diff_stream_->SetInputData(const_cast<uint8_t*>(patch_data) + offset,
70 diff_len)) {
71 LOG(ERROR) << "Failed to init ctrl stream, diff_len: " << diff_len << endl;
72 return false;
73 }
74
75 offset += diff_len;
76 if (!extra_stream_->SetInputData(const_cast<uint8_t*>(patch_data) + offset,
77 patch_size - offset)) {
78 LOG(ERROR) << "Failed to init extra stream, extra_offset: " << offset
79 << ", patch_size: " << patch_size << endl;
80 return false;
81 }
82 return true;
83}
84
85bool BsdiffPatchReader::ParseControlEntry(ControlEntry* control_entry) {
86 if (!control_entry)
87 return false;
88
89 uint8_t buf[8];
90 if (!ctrl_stream_->Read(buf, 8))
91 return false;
92 int64_t diff_size = ParseInt64(buf);
93
94 if (!ctrl_stream_->Read(buf, 8))
95 return false;
96 int64_t extra_size = ParseInt64(buf);
97
98 // Sanity check.
99 if (diff_size < 0 || extra_size < 0) {
100 LOG(ERROR) << "Corrupt patch; diff_size: " << diff_size
101 << ", extra_size: " << extra_size << endl;
102 return false;
103 }
104
105 control_entry->diff_size = diff_size;
106 control_entry->extra_size = extra_size;
107
108 if (!ctrl_stream_->Read(buf, 8))
109 return false;
110 control_entry->offset_increment = ParseInt64(buf);
111
112 return true;
113}
114
115bool BsdiffPatchReader::ReadDiffStream(uint8_t* buf, size_t size) {
116 return diff_stream_->Read(buf, size);
117}
118
119bool BsdiffPatchReader::ReadExtraStream(uint8_t* buf, size_t size) {
120 return extra_stream_->Read(buf, size);
121}
122
123bool BsdiffPatchReader::Finish() {
124 if (!ctrl_stream_->Close()) {
125 LOG(ERROR) << "Failed to close the control stream";
126 return false;
127 }
128
129 if (!diff_stream_->Close()) {
130 LOG(ERROR) << "Failed to close the diff stream";
131 return false;
132 }
133
134 if (!extra_stream_->Close()) {
135 LOG(ERROR) << "Failed to close the extra stream";
136 return false;
137 }
138 return true;
139}
140
141} // namespace bsdiff