blob: 6e9c325c1a324c074d2e6b21c31e4e9c78e76882 [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
34bool BsdiffPatchWriter::Open(const std::string& patch_filename) {
35 fp_ = fopen(patch_filename.c_str(), "w");
36 if (!fp_) {
37 LOG(ERROR) << "Opening " << patch_filename << endl;
38 return false;
39 }
40 return true;
41}
42
43bool BsdiffPatchWriter::AddControlEntry(const ControlEntry& entry) {
44 if (entry.diff_size > kMaxEncodedUint64Value) {
45 LOG(ERROR) << "Encoding value out of range " << entry.diff_size << endl;
46 return false;
47 }
48
49 if (entry.extra_size > kMaxEncodedUint64Value) {
50 LOG(ERROR) << "Encoding value out of range " << entry.extra_size << endl;
51 return false;
52 }
53
54 if (written_output_ + entry.diff_size + entry.extra_size > new_size_) {
55 LOG(ERROR) << "Wrote more output than the declared new_size" << endl;
56 return false;
57 }
58
59 if (entry.diff_size > 0 &&
60 (old_pos_ < 0 || static_cast<uint64_t>(old_pos_) >= old_size_)) {
61 LOG(ERROR) << "The pointer in the old stream (" << old_pos_
62 << ") is out of bounds [0, " << old_size_ << ")" << endl;
63 return false;
64 }
65
66 // Generate the 24 byte control entry.
67 uint8_t buf[24];
68 EncodeInt64(entry.diff_size, buf);
69 EncodeInt64(entry.extra_size, buf + 8);
70 EncodeInt64(entry.offset_increment, buf + 16);
71 if (!ctrl_stream_.Write(buf, sizeof(buf)))
72 return false;
73
74 // Generate the diff stream.
75 std::vector<uint8_t> diff(entry.diff_size);
76 for (uint64_t i = 0; i < entry.diff_size; ++i) {
77 diff[i] = new_buf_[written_output_ + i] - old_buf_[old_pos_ + i];
78 }
79 if (!diff_stream_.Write(diff.data(), diff.size()))
80 return false;
81
82 if (!extra_stream_.Write(new_buf_ + written_output_ + entry.diff_size,
83 entry.extra_size))
84 return false;
85
86 old_pos_ += entry.diff_size + entry.offset_increment;
87 written_output_ += entry.diff_size + entry.extra_size;
88 return true;
89}
90
91bool BsdiffPatchWriter::Close() {
92 if (!fp_) {
93 LOG(ERROR) << "File not open." << endl;
94 return false;
95 }
96 if (written_output_ != new_size_) {
97 LOG(ERROR) << "Close() called but not all the output was written" << endl;
98 return false;
99 }
100
101 if (!ctrl_stream_.Finish() || !diff_stream_.Finish() ||
102 !extra_stream_.Finish())
103 return false;
104
105 auto ctrl_data = ctrl_stream_.GetCompressedData();
106 auto diff_data = diff_stream_.GetCompressedData();
107 auto extra_data = extra_stream_.GetCompressedData();
108
109 if (!WriteHeader(ctrl_data.size(), diff_data.size()))
110 return false;
111
112 if (fwrite(ctrl_data.data(), 1, ctrl_data.size(), fp_) != ctrl_data.size()) {
113 LOG(ERROR) << "Writing ctrl_data." << endl;
114 return false;
115 }
116 if (fwrite(diff_data.data(), 1, diff_data.size(), fp_) != diff_data.size()) {
117 LOG(ERROR) << "Writing diff_data." << endl;
118 return false;
119 }
120 if (fwrite(extra_data.data(), 1, extra_data.size(), fp_) !=
121 extra_data.size()) {
122 LOG(ERROR) << "Writing extra_data." << endl;
123 return false;
124 }
125 if (fclose(fp_) != 0) {
126 LOG(ERROR) << "Closing the patch file." << endl;
127 return false;
128 }
129 fp_ = nullptr;
130 return true;
131}
132
133bool BsdiffPatchWriter::WriteHeader(uint64_t ctrl_size, uint64_t diff_size) {
134 /* Header format is
135 * 0 8 "BSDIFF40"
136 * 8 8 length of bzip2ed ctrl block
137 * 16 8 length of bzip2ed diff block
138 * 24 8 length of new file
139 *
140 * File format is
141 * 0 32 Header
142 * 32 ?? Bzip2ed ctrl block
143 * ?? ?? Bzip2ed diff block
144 * ?? ?? Bzip2ed extra block
145 */
146 uint8_t header[32];
147 memcpy(header, kMagicHeader, 8);
148 EncodeInt64(ctrl_size, header + 8);
149 EncodeInt64(diff_size, header + 16);
150 EncodeInt64(new_size_, header + 24);
151 if (fwrite(header, sizeof(header), 1, fp_) != 1) {
152 LOG(ERROR) << "writing to the patch file" << endl;
153 return false;
154 }
155 return true;
156}
157
158} // namespace bsdiff