blob: 4ed9a50141a4d56023d8a7ac7034c646e2088ba6 [file] [log] [blame]
Alex Deymoa28e0192017-09-08 14:21:05 +02001// 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
Alex Deymodcd423b2017-09-13 20:54:24 +02005#include "bsdiff/patch_writer.h"
Alex Deymoa28e0192017-09-08 14:21:05 +02006
7#include <string.h>
8
Alex Deymodcd423b2017-09-13 20:54:24 +02009#include "bsdiff/logging.h"
Alex Deymoa28e0192017-09-08 14:21:05 +020010
11using std::endl;
12
13namespace {
14
15// The maximum positive number that we should encode. A number larger than this
16// for unsigned fields will be interpreted as a negative value and thus a
17// corrupt patch.
18const uint64_t kMaxEncodedUint64Value = (1ULL << 63) - 1;
19
20constexpr uint8_t kMagicHeader[] = "BSDIFF40";
21
22void EncodeInt64(int64_t x, uint8_t* buf) {
23 uint64_t y = x < 0 ? (1ULL << 63ULL) - x : x;
24 for (int i = 0; i < 8; ++i) {
25 buf[i] = y & 0xff;
26 y /= 256;
27 }
28}
29
30} // namespace
31
32namespace bsdiff {
33
Alex Deymo538a75d2017-09-27 15:34:59 +020034bool BsdiffPatchWriter::InitializeBuffers(const uint8_t* old_buf,
35 uint64_t old_size,
36 const uint8_t* new_buf,
37 uint64_t new_size) {
38 old_buf_ = old_buf;
39 old_size_ = old_size;
40 new_buf_ = new_buf;
41 new_size_ = new_size;
42
43 fp_ = fopen(patch_filename_.c_str(), "w");
Alex Deymoa28e0192017-09-08 14:21:05 +020044 if (!fp_) {
Alex Deymo538a75d2017-09-27 15:34:59 +020045 LOG(ERROR) << "Opening " << patch_filename_ << endl;
Alex Deymoa28e0192017-09-08 14:21:05 +020046 return false;
47 }
48 return true;
49}
50
51bool BsdiffPatchWriter::AddControlEntry(const ControlEntry& entry) {
52 if (entry.diff_size > kMaxEncodedUint64Value) {
53 LOG(ERROR) << "Encoding value out of range " << entry.diff_size << endl;
54 return false;
55 }
56
57 if (entry.extra_size > kMaxEncodedUint64Value) {
58 LOG(ERROR) << "Encoding value out of range " << entry.extra_size << endl;
59 return false;
60 }
61
62 if (written_output_ + entry.diff_size + entry.extra_size > new_size_) {
63 LOG(ERROR) << "Wrote more output than the declared new_size" << endl;
64 return false;
65 }
66
67 if (entry.diff_size > 0 &&
68 (old_pos_ < 0 || static_cast<uint64_t>(old_pos_) >= old_size_)) {
69 LOG(ERROR) << "The pointer in the old stream (" << old_pos_
70 << ") is out of bounds [0, " << old_size_ << ")" << endl;
71 return false;
72 }
73
74 // Generate the 24 byte control entry.
75 uint8_t buf[24];
76 EncodeInt64(entry.diff_size, buf);
77 EncodeInt64(entry.extra_size, buf + 8);
78 EncodeInt64(entry.offset_increment, buf + 16);
79 if (!ctrl_stream_.Write(buf, sizeof(buf)))
80 return false;
81
82 // Generate the diff stream.
83 std::vector<uint8_t> diff(entry.diff_size);
84 for (uint64_t i = 0; i < entry.diff_size; ++i) {
85 diff[i] = new_buf_[written_output_ + i] - old_buf_[old_pos_ + i];
86 }
87 if (!diff_stream_.Write(diff.data(), diff.size()))
88 return false;
89
90 if (!extra_stream_.Write(new_buf_ + written_output_ + entry.diff_size,
91 entry.extra_size))
92 return false;
93
94 old_pos_ += entry.diff_size + entry.offset_increment;
95 written_output_ += entry.diff_size + entry.extra_size;
96 return true;
97}
98
99bool BsdiffPatchWriter::Close() {
100 if (!fp_) {
101 LOG(ERROR) << "File not open." << endl;
102 return false;
103 }
104 if (written_output_ != new_size_) {
105 LOG(ERROR) << "Close() called but not all the output was written" << endl;
106 return false;
107 }
108
109 if (!ctrl_stream_.Finish() || !diff_stream_.Finish() ||
110 !extra_stream_.Finish())
111 return false;
112
113 auto ctrl_data = ctrl_stream_.GetCompressedData();
114 auto diff_data = diff_stream_.GetCompressedData();
115 auto extra_data = extra_stream_.GetCompressedData();
116
117 if (!WriteHeader(ctrl_data.size(), diff_data.size()))
118 return false;
119
120 if (fwrite(ctrl_data.data(), 1, ctrl_data.size(), fp_) != ctrl_data.size()) {
121 LOG(ERROR) << "Writing ctrl_data." << endl;
122 return false;
123 }
124 if (fwrite(diff_data.data(), 1, diff_data.size(), fp_) != diff_data.size()) {
125 LOG(ERROR) << "Writing diff_data." << endl;
126 return false;
127 }
128 if (fwrite(extra_data.data(), 1, extra_data.size(), fp_) !=
129 extra_data.size()) {
130 LOG(ERROR) << "Writing extra_data." << endl;
131 return false;
132 }
133 if (fclose(fp_) != 0) {
134 LOG(ERROR) << "Closing the patch file." << endl;
135 return false;
136 }
137 fp_ = nullptr;
138 return true;
139}
140
141bool BsdiffPatchWriter::WriteHeader(uint64_t ctrl_size, uint64_t diff_size) {
142 /* Header format is
143 * 0 8 "BSDIFF40"
144 * 8 8 length of bzip2ed ctrl block
145 * 16 8 length of bzip2ed diff block
146 * 24 8 length of new file
147 *
148 * File format is
149 * 0 32 Header
150 * 32 ?? Bzip2ed ctrl block
151 * ?? ?? Bzip2ed diff block
152 * ?? ?? Bzip2ed extra block
153 */
154 uint8_t header[32];
155 memcpy(header, kMagicHeader, 8);
156 EncodeInt64(ctrl_size, header + 8);
157 EncodeInt64(diff_size, header + 16);
158 EncodeInt64(new_size_, header + 24);
159 if (fwrite(header, sizeof(header), 1, fp_) != 1) {
160 LOG(ERROR) << "writing to the patch file" << endl;
161 return false;
162 }
163 return true;
164}
165
166} // namespace bsdiff