blob: 491fc0130b7800787b353dae5fce92555aa5c8b3 [file] [log] [blame]
The Android Open Source Projectc285fea2009-03-03 19:29:20 -08001/*-
2 * Copyright 2003-2005 Colin Percival
3 * All rights reserved
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted providing that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#if 0
28__FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:06 cperciva Exp $");
29#endif
30
Alex Deymoddf9db52017-03-02 16:10:41 -080031#include "bsdiff/bspatch.h"
Alex Deymob870eb52015-10-14 21:39:04 -070032
Sen Jiang5b372b62016-03-28 16:14:35 -070033#include <errno.h>
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080034#include <fcntl.h>
Gilad Arnold99b53742013-04-30 09:24:14 -070035#include <inttypes.h>
Gilad Arnold99b53742013-04-30 09:24:14 -070036#include <stdlib.h>
37#include <string.h>
Sen Jiang5b372b62016-03-28 16:14:35 -070038#include <sys/stat.h>
Alex Deymob870eb52015-10-14 21:39:04 -070039#include <sys/types.h>
Alex Deymodcd423b2017-09-13 20:54:24 +020040#include <unistd.h>
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080041
Alex Deymo437b7af2015-10-14 20:13:58 -070042#include <algorithm>
Alex Deymodcd423b2017-09-13 20:54:24 +020043#include <memory>
Sen Jiang5b372b62016-03-28 16:14:35 -070044#include <vector>
Alex Deymo437b7af2015-10-14 20:13:58 -070045
Alex Deymodcd423b2017-09-13 20:54:24 +020046#include "bsdiff/buffer_file.h"
Tianjie Xu65288122017-10-13 15:10:58 -070047#include "bsdiff/control_entry.h"
Alex Deymodcd423b2017-09-13 20:54:24 +020048#include "bsdiff/extents.h"
Alex Deymoddf9db52017-03-02 16:10:41 -080049#include "bsdiff/extents_file.h"
Alex Deymodcd423b2017-09-13 20:54:24 +020050#include "bsdiff/file.h"
Alex Deymoddf9db52017-03-02 16:10:41 -080051#include "bsdiff/file_interface.h"
Alex Deymodcd423b2017-09-13 20:54:24 +020052#include "bsdiff/memory_file.h"
Tianjie Xu65288122017-10-13 15:10:58 -070053#include "bsdiff/patch_reader.h"
Alex Deymodcd423b2017-09-13 20:54:24 +020054#include "bsdiff/sink_file.h"
Tianjie Xu65288122017-10-13 15:10:58 -070055#include "bsdiff/utils.h"
Zdenek Behan339e0ee2010-06-14 12:50:53 -070056
Alex Deymo20891f92015-10-12 17:28:04 -070057namespace {
Tianjie Xu65288122017-10-13 15:10:58 -070058// Read the data in |stream| and write |size| decompressed data to |file|;
59// using the buffer in |buf| with size |buf_size|.
60// Returns 0 on success, 1 on I/O error and 2 on data error.
61int ReadStreamAndWriteAll(
62 const std::unique_ptr<bsdiff::FileInterface>& file,
63 size_t size,
64 uint8_t* buf,
65 size_t buf_size,
66 const std::function<bool(uint8_t*, size_t)>& read_func) {
Sen Jiang716d5692016-05-09 16:43:34 -070067 while (size > 0) {
Tianjie Xu65288122017-10-13 15:10:58 -070068 size_t bytes_to_output = std::min(size, buf_size);
69 if (!read_func(buf, bytes_to_output)) {
70 fprintf(stderr, "Failed to read stream.\n");
Sen Jiangb552c792017-01-20 14:07:49 -080071 return 2;
72 }
Tianjie Xu65288122017-10-13 15:10:58 -070073
74 if (!WriteAll(file, buf, bytes_to_output)) {
Sen Jiangb552c792017-01-20 14:07:49 -080075 perror("WriteAll() failed");
76 return 1;
77 }
Tianjie Xu65288122017-10-13 15:10:58 -070078 size -= bytes_to_output;
Sen Jiang5b372b62016-03-28 16:14:35 -070079 }
Sen Jiangb552c792017-01-20 14:07:49 -080080 return 0;
Sen Jiang5b372b62016-03-28 16:14:35 -070081}
82
Alex Deymo20891f92015-10-12 17:28:04 -070083} // namespace
84
85namespace bsdiff {
86
Sen Jiang716d5692016-05-09 16:43:34 -070087bool ReadAll(const std::unique_ptr<FileInterface>& file,
88 uint8_t* data,
89 size_t size) {
90 size_t offset = 0, read;
91 while (offset < size) {
92 if (!file->Read(data + offset, size - offset, &read) || read == 0)
93 return false;
94 offset += read;
95 }
96 return true;
97}
98
Sen Jiang5b372b62016-03-28 16:14:35 -070099bool WriteAll(const std::unique_ptr<FileInterface>& file,
100 const uint8_t* data,
101 size_t size) {
102 size_t offset = 0, written;
103 while (offset < size) {
Sen Jiangb14bb552016-04-11 16:08:03 -0700104 if (!file->Write(data + offset, size - offset, &written) || written == 0)
Sen Jiang5b372b62016-03-28 16:14:35 -0700105 return false;
106 offset += written;
107 }
108 return true;
109}
110
111bool IsOverlapping(const char* old_filename,
112 const char* new_filename,
113 const std::vector<ex_t>& old_extents,
114 const std::vector<ex_t>& new_extents) {
115 struct stat old_stat, new_stat;
116 if (stat(new_filename, &new_stat) == -1) {
117 if (errno == ENOENT)
118 return false;
Sen Jiangb552c792017-01-20 14:07:49 -0800119 fprintf(stderr, "Error stat the new file %s: %s\n", new_filename,
120 strerror(errno));
121 return true;
Sen Jiang5b372b62016-03-28 16:14:35 -0700122 }
Sen Jiangb552c792017-01-20 14:07:49 -0800123 if (stat(old_filename, &old_stat) == -1) {
124 fprintf(stderr, "Error stat the old file %s: %s\n", old_filename,
125 strerror(errno));
126 return true;
127 }
Sen Jiang5b372b62016-03-28 16:14:35 -0700128
129 if (old_stat.st_dev != new_stat.st_dev || old_stat.st_ino != new_stat.st_ino)
130 return false;
131
132 if (old_extents.empty() && new_extents.empty())
133 return true;
134
135 for (ex_t old_ex : old_extents)
136 for (ex_t new_ex : new_extents)
137 if (static_cast<uint64_t>(old_ex.off) < new_ex.off + new_ex.len &&
138 static_cast<uint64_t>(new_ex.off) < old_ex.off + old_ex.len)
139 return true;
140
141 return false;
142}
143
Sen Jiangb552c792017-01-20 14:07:49 -0800144// Patch |old_filename| with |patch_filename| and save it to |new_filename|.
145// |old_extents| and |new_extents| are comma-separated lists of "offset:length"
146// extents of |old_filename| and |new_filename|.
147// Returns 0 on success, 1 on I/O error and 2 on data error.
148int bspatch(const char* old_filename,
149 const char* new_filename,
150 const char* patch_filename,
151 const char* old_extents,
152 const char* new_extents) {
Sen Jiang716d5692016-05-09 16:43:34 -0700153 std::unique_ptr<FileInterface> patch_file =
154 File::FOpen(patch_filename, O_RDONLY);
Sen Jiangb552c792017-01-20 14:07:49 -0800155 if (!patch_file) {
156 fprintf(stderr, "Error opening the patch file %s: %s\n", patch_filename,
157 strerror(errno));
158 return 1;
159 }
Sen Jiang716d5692016-05-09 16:43:34 -0700160 uint64_t patch_size;
161 patch_file->GetSize(&patch_size);
162 std::vector<uint8_t> patch(patch_size);
Sen Jiangb552c792017-01-20 14:07:49 -0800163 if (!ReadAll(patch_file, patch.data(), patch_size)) {
164 fprintf(stderr, "Error reading the patch file %s: %s\n", patch_filename,
165 strerror(errno));
166 return 1;
167 }
Sen Jiang716d5692016-05-09 16:43:34 -0700168 patch_file.reset();
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800169
Sen Jiang62d5e482017-01-24 16:25:25 -0800170 return bspatch(old_filename, new_filename, patch.data(), patch_size,
171 old_extents, new_extents);
172}
173
174// Patch |old_filename| with |patch_data| and save it to |new_filename|.
175// |old_extents| and |new_extents| are comma-separated lists of "offset:length"
176// extents of |old_filename| and |new_filename|.
177// Returns 0 on success, 1 on I/O error and 2 on data error.
178int bspatch(const char* old_filename,
179 const char* new_filename,
180 const uint8_t* patch_data,
181 size_t patch_size,
182 const char* old_extents,
183 const char* new_extents) {
Alex Deymob870eb52015-10-14 21:39:04 -0700184 int using_extents = (old_extents != NULL || new_extents != NULL);
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800185
Alex Deymob870eb52015-10-14 21:39:04 -0700186 // Open input file for reading.
Alex Deymo437b7af2015-10-14 20:13:58 -0700187 std::unique_ptr<FileInterface> old_file = File::FOpen(old_filename, O_RDONLY);
Sen Jiangb552c792017-01-20 14:07:49 -0800188 if (!old_file) {
189 fprintf(stderr, "Error opening the old file %s: %s\n", old_filename,
190 strerror(errno));
191 return 1;
192 }
Alex Deymo437b7af2015-10-14 20:13:58 -0700193
Sen Jiang5b372b62016-03-28 16:14:35 -0700194 std::vector<ex_t> parsed_old_extents;
Alex Deymob870eb52015-10-14 21:39:04 -0700195 if (using_extents) {
Sen Jiangb552c792017-01-20 14:07:49 -0800196 if (!ParseExtentStr(old_extents, &parsed_old_extents)) {
197 fprintf(stderr, "Error parsing the old extents\n");
198 return 2;
199 }
Alex Deymo437b7af2015-10-14 20:13:58 -0700200 old_file.reset(new ExtentsFile(std::move(old_file), parsed_old_extents));
201 }
202
Sen Jiang5b372b62016-03-28 16:14:35 -0700203 // Open output file for writing.
204 std::unique_ptr<FileInterface> new_file =
205 File::FOpen(new_filename, O_CREAT | O_WRONLY);
Sen Jiangb552c792017-01-20 14:07:49 -0800206 if (!new_file) {
207 fprintf(stderr, "Error opening the new file %s: %s\n", new_filename,
208 strerror(errno));
209 return 1;
210 }
Sen Jiang5b372b62016-03-28 16:14:35 -0700211
212 std::vector<ex_t> parsed_new_extents;
213 if (using_extents) {
Sen Jiangb552c792017-01-20 14:07:49 -0800214 if (!ParseExtentStr(new_extents, &parsed_new_extents)) {
215 fprintf(stderr, "Error parsing the new extents\n");
216 return 2;
217 }
Sen Jiang5b372b62016-03-28 16:14:35 -0700218 new_file.reset(new ExtentsFile(std::move(new_file), parsed_new_extents));
219 }
220
221 if (IsOverlapping(old_filename, new_filename, parsed_old_extents,
222 parsed_new_extents)) {
223 // New and old file is overlapping, we can not stream output to new file,
Sen Jiang716d5692016-05-09 16:43:34 -0700224 // cache it in a buffer and write to the file at the end.
Sen Jiang62d5e482017-01-24 16:25:25 -0800225 uint64_t newsize = ParseInt64(patch_data + 24);
Sen Jiang716d5692016-05-09 16:43:34 -0700226 new_file.reset(new BufferFile(std::move(new_file), newsize));
Sen Jiang5b372b62016-03-28 16:14:35 -0700227 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800228
Sen Jiang62d5e482017-01-24 16:25:25 -0800229 return bspatch(old_file, new_file, patch_data, patch_size);
Sen Jiang716d5692016-05-09 16:43:34 -0700230}
231
Sen Jiangb552c792017-01-20 14:07:49 -0800232// Patch |old_data| with |patch_data| and save it by calling sink function.
233// Returns 0 on success, 1 on I/O error and 2 on data error.
Sen Jiang716d5692016-05-09 16:43:34 -0700234int bspatch(const uint8_t* old_data,
235 size_t old_size,
236 const uint8_t* patch_data,
237 size_t patch_size,
238 const sink_func& sink) {
239 std::unique_ptr<FileInterface> old_file(new MemoryFile(old_data, old_size));
240 std::unique_ptr<FileInterface> new_file(new SinkFile(sink));
241
242 return bspatch(old_file, new_file, patch_data, patch_size);
243}
244
Sen Jiangb552c792017-01-20 14:07:49 -0800245// Patch |old_file| with |patch_data| and save it to |new_file|.
246// Returns 0 on success, 1 on I/O error and 2 on data error.
Sen Jiang716d5692016-05-09 16:43:34 -0700247int bspatch(const std::unique_ptr<FileInterface>& old_file,
248 const std::unique_ptr<FileInterface>& new_file,
249 const uint8_t* patch_data,
250 size_t patch_size) {
Tianjie Xu65288122017-10-13 15:10:58 -0700251 BsdiffPatchReader patch_reader;
252 if (!patch_reader.Init(patch_data, patch_size)) {
253 fprintf(stderr, "Failed to initialize patch reader\n");
Sen Jiangb552c792017-01-20 14:07:49 -0800254 return 2;
255 }
Sen Jiang716d5692016-05-09 16:43:34 -0700256
Tianjie Xu65288122017-10-13 15:10:58 -0700257 uint64_t old_file_size;
258 if (!old_file->GetSize(&old_file_size)) {
Sen Jiangb552c792017-01-20 14:07:49 -0800259 fprintf(stderr, "Cannot obtain the size of old file.\n");
260 return 1;
261 }
Sen Jiang716d5692016-05-09 16:43:34 -0700262
Alex Deymo437b7af2015-10-14 20:13:58 -0700263 // The oldpos can be negative, but the new pos is only incremented linearly.
264 int64_t oldpos = 0;
265 uint64_t newpos = 0;
Tianjie Xu65288122017-10-13 15:10:58 -0700266 std::vector<uint8_t> old_buf(1024 * 1024);
267 std::vector<uint8_t> new_buf(1024 * 1024);
268 uint64_t old_file_pos = 0;
269 while (newpos < patch_reader.new_file_size()) {
270 ControlEntry control_entry(0, 0, 0);
271 if (!patch_reader.ParseControlEntry(&control_entry)) {
272 fprintf(stderr, "Failed to read control stream\n");
Sen Jiangb552c792017-01-20 14:07:49 -0800273 return 2;
274 }
Doug Zongker4d054792014-05-13 08:37:06 -0700275
Alex Deymob870eb52015-10-14 21:39:04 -0700276 // Sanity-check.
Tianjie Xu65288122017-10-13 15:10:58 -0700277 if (newpos + control_entry.diff_size > patch_reader.new_file_size()) {
Sen Jiangb552c792017-01-20 14:07:49 -0800278 fprintf(stderr, "Corrupt patch.\n");
279 return 2;
280 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800281
Sen Jiangb552c792017-01-20 14:07:49 -0800282 int ret = 0;
Alex Deymob870eb52015-10-14 21:39:04 -0700283 // Add old data to diff string. It is enough to fseek once, at
284 // the beginning of the sequence, to avoid unnecessary overhead.
Tianjie Xu65288122017-10-13 15:10:58 -0700285 int64_t seek_offset = oldpos;
286 if (seek_offset < 0) {
Sen Jiang5b372b62016-03-28 16:14:35 -0700287 // Write diff block directly to new file without adding old data,
288 // because we will skip part where |oldpos| < 0.
Tianjie Xu65288122017-10-13 15:10:58 -0700289 ret = ReadStreamAndWriteAll(
290 new_file, oldpos - old_file_size, new_buf.data(), new_buf.size(),
291 std::bind(&BsdiffPatchReader::ReadDiffStream, &patch_reader,
292 std::placeholders::_1, std::placeholders::_2));
Sen Jiangb552c792017-01-20 14:07:49 -0800293 if (ret)
294 return ret;
Tianjie Xu65288122017-10-13 15:10:58 -0700295 seek_offset = 0;
Alex Deymob870eb52015-10-14 21:39:04 -0700296 }
Sen Jiang5b372b62016-03-28 16:14:35 -0700297
Tianjie Xu65288122017-10-13 15:10:58 -0700298 // We just checked that |seek_offset| is not negative.
299 if (static_cast<uint64_t>(seek_offset) != old_file_pos &&
300 !old_file->Seek(seek_offset)) {
301 fprintf(stderr, "Error seeking input file to offset %" PRId64 ": %s\n",
302 seek_offset, strerror(errno));
Sen Jiangb552c792017-01-20 14:07:49 -0800303 return 1;
304 }
Alex Deymo437b7af2015-10-14 20:13:58 -0700305
Tianjie Xu65288122017-10-13 15:10:58 -0700306 old_file_pos =
307 std::min<uint64_t>(oldpos + control_entry.diff_size, old_file_size);
308 size_t chunk_size = old_file_pos - seek_offset;
Alex Deymo437b7af2015-10-14 20:13:58 -0700309 while (chunk_size > 0) {
310 size_t read_bytes;
Sen Jiang5b372b62016-03-28 16:14:35 -0700311 size_t bytes_to_read = std::min(chunk_size, old_buf.size());
Sen Jiangb552c792017-01-20 14:07:49 -0800312 if (!old_file->Read(old_buf.data(), bytes_to_read, &read_bytes)) {
313 perror("Error reading from input file");
314 return 1;
315 }
316 if (!read_bytes) {
317 fprintf(stderr, "EOF reached while reading from input file.\n");
318 return 2;
319 }
Sen Jiang5b372b62016-03-28 16:14:35 -0700320 // Read same amount of bytes from diff block
Tianjie Xu65288122017-10-13 15:10:58 -0700321 if (!patch_reader.ReadDiffStream(new_buf.data(), read_bytes)) {
Sen Jiangb552c792017-01-20 14:07:49 -0800322 fprintf(stderr, "Failed to read diff stream.\n");
323 return 2;
324 }
Sen Jiangd87c8352015-11-20 16:14:36 -0800325 // new_buf already has data from diff block, adds old data to it.
326 for (size_t k = 0; k < read_bytes; k++)
Sen Jiang5b372b62016-03-28 16:14:35 -0700327 new_buf[k] += old_buf[k];
Sen Jiangb552c792017-01-20 14:07:49 -0800328 if (!WriteAll(new_file, new_buf.data(), read_bytes)) {
329 perror("Error writing to new file");
330 return 1;
331 }
Alex Deymo437b7af2015-10-14 20:13:58 -0700332 chunk_size -= read_bytes;
Alex Deymob870eb52015-10-14 21:39:04 -0700333 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800334
Alex Deymob870eb52015-10-14 21:39:04 -0700335 // Adjust pointers.
Tianjie Xu65288122017-10-13 15:10:58 -0700336 newpos += control_entry.diff_size;
337 oldpos += control_entry.diff_size;
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800338
Tianjie Xu65288122017-10-13 15:10:58 -0700339 if (oldpos > static_cast<int64_t>(old_file_size)) {
Sen Jiang5b372b62016-03-28 16:14:35 -0700340 // Write diff block directly to new file without adding old data,
Tianjie Xu65288122017-10-13 15:10:58 -0700341 // because we skipped part where |oldpos| > old_file_size.
342 ret = ReadStreamAndWriteAll(
343 new_file, oldpos - old_file_size, new_buf.data(), new_buf.size(),
344 std::bind(&BsdiffPatchReader::ReadDiffStream, &patch_reader,
345 std::placeholders::_1, std::placeholders::_2));
Sen Jiangb552c792017-01-20 14:07:49 -0800346 if (ret)
347 return ret;
Sen Jiang5b372b62016-03-28 16:14:35 -0700348 }
349
Alex Deymob870eb52015-10-14 21:39:04 -0700350 // Sanity-check.
Tianjie Xu65288122017-10-13 15:10:58 -0700351 if (newpos + control_entry.extra_size > patch_reader.new_file_size()) {
Sen Jiangb552c792017-01-20 14:07:49 -0800352 fprintf(stderr, "Corrupt patch.\n");
353 return 2;
354 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800355
Sen Jiang5b372b62016-03-28 16:14:35 -0700356 // Read extra block.
Tianjie Xu65288122017-10-13 15:10:58 -0700357 ret = ReadStreamAndWriteAll(
358 new_file, control_entry.extra_size, new_buf.data(), new_buf.size(),
359 std::bind(&BsdiffPatchReader::ReadExtraStream, &patch_reader,
360 std::placeholders::_1, std::placeholders::_2));
Sen Jiangb552c792017-01-20 14:07:49 -0800361 if (ret)
362 return ret;
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800363
Alex Deymob870eb52015-10-14 21:39:04 -0700364 // Adjust pointers.
Tianjie Xu65288122017-10-13 15:10:58 -0700365 newpos += control_entry.extra_size;
366 oldpos += control_entry.offset_increment;
Sen Jiang5b372b62016-03-28 16:14:35 -0700367 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800368
Alex Deymob870eb52015-10-14 21:39:04 -0700369 // Close input file.
Alex Deymo437b7af2015-10-14 20:13:58 -0700370 old_file->Close();
Gilad Arnold99b53742013-04-30 09:24:14 -0700371
Tianjie Xu65288122017-10-13 15:10:58 -0700372 if (!patch_reader.Finish()) {
373 fprintf(stderr, "Failed to finish the patch reader\n");
374 return 2;
375 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800376
Sen Jiangb552c792017-01-20 14:07:49 -0800377 if (!new_file->Close()) {
378 perror("Error closing new file");
379 return 1;
380 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800381
Alex Deymob870eb52015-10-14 21:39:04 -0700382 return 0;
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800383}
Alex Deymo20891f92015-10-12 17:28:04 -0700384
385} // namespace bsdiff