blob: 2aedd437dcd9e6f3f0b67663e2895bd55b34981a [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>
Alex Deymoea72d9f2017-10-26 13:15:13 +020036#include <stdio.h>
Gilad Arnold99b53742013-04-30 09:24:14 -070037#include <stdlib.h>
38#include <string.h>
Sen Jiang5b372b62016-03-28 16:14:35 -070039#include <sys/stat.h>
Alex Deymob870eb52015-10-14 21:39:04 -070040#include <sys/types.h>
Alex Deymodcd423b2017-09-13 20:54:24 +020041#include <unistd.h>
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080042
Alex Deymo437b7af2015-10-14 20:13:58 -070043#include <algorithm>
Alex Deymodcd423b2017-09-13 20:54:24 +020044#include <memory>
Sen Jiang5b372b62016-03-28 16:14:35 -070045#include <vector>
Alex Deymo437b7af2015-10-14 20:13:58 -070046
Alex Deymodcd423b2017-09-13 20:54:24 +020047#include "bsdiff/buffer_file.h"
Tianjie Xu65288122017-10-13 15:10:58 -070048#include "bsdiff/control_entry.h"
Alex Deymodcd423b2017-09-13 20:54:24 +020049#include "bsdiff/extents.h"
Alex Deymoddf9db52017-03-02 16:10:41 -080050#include "bsdiff/extents_file.h"
Alex Deymodcd423b2017-09-13 20:54:24 +020051#include "bsdiff/file.h"
Alex Deymoddf9db52017-03-02 16:10:41 -080052#include "bsdiff/file_interface.h"
Alex Deymodcd423b2017-09-13 20:54:24 +020053#include "bsdiff/memory_file.h"
Tianjie Xu65288122017-10-13 15:10:58 -070054#include "bsdiff/patch_reader.h"
Alex Deymodcd423b2017-09-13 20:54:24 +020055#include "bsdiff/sink_file.h"
Tianjie Xu65288122017-10-13 15:10:58 -070056#include "bsdiff/utils.h"
Zdenek Behan339e0ee2010-06-14 12:50:53 -070057
Alex Deymo20891f92015-10-12 17:28:04 -070058namespace {
Tianjie Xu65288122017-10-13 15:10:58 -070059// Read the data in |stream| and write |size| decompressed data to |file|;
60// using the buffer in |buf| with size |buf_size|.
61// Returns 0 on success, 1 on I/O error and 2 on data error.
62int ReadStreamAndWriteAll(
63 const std::unique_ptr<bsdiff::FileInterface>& file,
64 size_t size,
65 uint8_t* buf,
66 size_t buf_size,
67 const std::function<bool(uint8_t*, size_t)>& read_func) {
Sen Jiang716d5692016-05-09 16:43:34 -070068 while (size > 0) {
Tianjie Xu65288122017-10-13 15:10:58 -070069 size_t bytes_to_output = std::min(size, buf_size);
70 if (!read_func(buf, bytes_to_output)) {
71 fprintf(stderr, "Failed to read stream.\n");
Sen Jiangb552c792017-01-20 14:07:49 -080072 return 2;
73 }
Tianjie Xu65288122017-10-13 15:10:58 -070074
75 if (!WriteAll(file, buf, bytes_to_output)) {
Sen Jiangb552c792017-01-20 14:07:49 -080076 perror("WriteAll() failed");
77 return 1;
78 }
Tianjie Xu65288122017-10-13 15:10:58 -070079 size -= bytes_to_output;
Sen Jiang5b372b62016-03-28 16:14:35 -070080 }
Sen Jiangb552c792017-01-20 14:07:49 -080081 return 0;
Sen Jiang5b372b62016-03-28 16:14:35 -070082}
83
Alex Deymo20891f92015-10-12 17:28:04 -070084} // namespace
85
86namespace bsdiff {
87
Sen Jiang716d5692016-05-09 16:43:34 -070088bool ReadAll(const std::unique_ptr<FileInterface>& file,
89 uint8_t* data,
90 size_t size) {
91 size_t offset = 0, read;
92 while (offset < size) {
93 if (!file->Read(data + offset, size - offset, &read) || read == 0)
94 return false;
95 offset += read;
96 }
97 return true;
98}
99
Sen Jiang5b372b62016-03-28 16:14:35 -0700100bool WriteAll(const std::unique_ptr<FileInterface>& file,
101 const uint8_t* data,
102 size_t size) {
103 size_t offset = 0, written;
104 while (offset < size) {
Sen Jiangb14bb552016-04-11 16:08:03 -0700105 if (!file->Write(data + offset, size - offset, &written) || written == 0)
Sen Jiang5b372b62016-03-28 16:14:35 -0700106 return false;
107 offset += written;
108 }
109 return true;
110}
111
112bool IsOverlapping(const char* old_filename,
113 const char* new_filename,
114 const std::vector<ex_t>& old_extents,
115 const std::vector<ex_t>& new_extents) {
116 struct stat old_stat, new_stat;
117 if (stat(new_filename, &new_stat) == -1) {
118 if (errno == ENOENT)
119 return false;
Sen Jiangb552c792017-01-20 14:07:49 -0800120 fprintf(stderr, "Error stat the new file %s: %s\n", new_filename,
121 strerror(errno));
122 return true;
Sen Jiang5b372b62016-03-28 16:14:35 -0700123 }
Sen Jiangb552c792017-01-20 14:07:49 -0800124 if (stat(old_filename, &old_stat) == -1) {
125 fprintf(stderr, "Error stat the old file %s: %s\n", old_filename,
126 strerror(errno));
127 return true;
128 }
Sen Jiang5b372b62016-03-28 16:14:35 -0700129
130 if (old_stat.st_dev != new_stat.st_dev || old_stat.st_ino != new_stat.st_ino)
131 return false;
132
133 if (old_extents.empty() && new_extents.empty())
134 return true;
135
136 for (ex_t old_ex : old_extents)
137 for (ex_t new_ex : new_extents)
138 if (static_cast<uint64_t>(old_ex.off) < new_ex.off + new_ex.len &&
139 static_cast<uint64_t>(new_ex.off) < old_ex.off + old_ex.len)
140 return true;
141
142 return false;
143}
144
Sen Jiangb552c792017-01-20 14:07:49 -0800145// Patch |old_filename| with |patch_filename| and save it to |new_filename|.
146// |old_extents| and |new_extents| are comma-separated lists of "offset:length"
147// extents of |old_filename| and |new_filename|.
148// Returns 0 on success, 1 on I/O error and 2 on data error.
149int bspatch(const char* old_filename,
150 const char* new_filename,
151 const char* patch_filename,
152 const char* old_extents,
153 const char* new_extents) {
Sen Jiang716d5692016-05-09 16:43:34 -0700154 std::unique_ptr<FileInterface> patch_file =
155 File::FOpen(patch_filename, O_RDONLY);
Sen Jiangb552c792017-01-20 14:07:49 -0800156 if (!patch_file) {
157 fprintf(stderr, "Error opening the patch file %s: %s\n", patch_filename,
158 strerror(errno));
159 return 1;
160 }
Sen Jiang716d5692016-05-09 16:43:34 -0700161 uint64_t patch_size;
162 patch_file->GetSize(&patch_size);
163 std::vector<uint8_t> patch(patch_size);
Sen Jiangb552c792017-01-20 14:07:49 -0800164 if (!ReadAll(patch_file, patch.data(), patch_size)) {
165 fprintf(stderr, "Error reading the patch file %s: %s\n", patch_filename,
166 strerror(errno));
167 return 1;
168 }
Sen Jiang716d5692016-05-09 16:43:34 -0700169 patch_file.reset();
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800170
Sen Jiang62d5e482017-01-24 16:25:25 -0800171 return bspatch(old_filename, new_filename, patch.data(), patch_size,
172 old_extents, new_extents);
173}
174
175// Patch |old_filename| with |patch_data| and save it to |new_filename|.
176// |old_extents| and |new_extents| are comma-separated lists of "offset:length"
177// extents of |old_filename| and |new_filename|.
178// Returns 0 on success, 1 on I/O error and 2 on data error.
179int bspatch(const char* old_filename,
180 const char* new_filename,
181 const uint8_t* patch_data,
182 size_t patch_size,
183 const char* old_extents,
184 const char* new_extents) {
Alex Deymob870eb52015-10-14 21:39:04 -0700185 int using_extents = (old_extents != NULL || new_extents != NULL);
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800186
Alex Deymob870eb52015-10-14 21:39:04 -0700187 // Open input file for reading.
Alex Deymo437b7af2015-10-14 20:13:58 -0700188 std::unique_ptr<FileInterface> old_file = File::FOpen(old_filename, O_RDONLY);
Sen Jiangb552c792017-01-20 14:07:49 -0800189 if (!old_file) {
190 fprintf(stderr, "Error opening the old file %s: %s\n", old_filename,
191 strerror(errno));
192 return 1;
193 }
Alex Deymo437b7af2015-10-14 20:13:58 -0700194
Sen Jiang5b372b62016-03-28 16:14:35 -0700195 std::vector<ex_t> parsed_old_extents;
Alex Deymob870eb52015-10-14 21:39:04 -0700196 if (using_extents) {
Sen Jiangb552c792017-01-20 14:07:49 -0800197 if (!ParseExtentStr(old_extents, &parsed_old_extents)) {
198 fprintf(stderr, "Error parsing the old extents\n");
199 return 2;
200 }
Alex Deymo437b7af2015-10-14 20:13:58 -0700201 old_file.reset(new ExtentsFile(std::move(old_file), parsed_old_extents));
202 }
203
Sen Jiang5b372b62016-03-28 16:14:35 -0700204 // Open output file for writing.
205 std::unique_ptr<FileInterface> new_file =
206 File::FOpen(new_filename, O_CREAT | O_WRONLY);
Sen Jiangb552c792017-01-20 14:07:49 -0800207 if (!new_file) {
208 fprintf(stderr, "Error opening the new file %s: %s\n", new_filename,
209 strerror(errno));
210 return 1;
211 }
Sen Jiang5b372b62016-03-28 16:14:35 -0700212
213 std::vector<ex_t> parsed_new_extents;
214 if (using_extents) {
Sen Jiangb552c792017-01-20 14:07:49 -0800215 if (!ParseExtentStr(new_extents, &parsed_new_extents)) {
216 fprintf(stderr, "Error parsing the new extents\n");
217 return 2;
218 }
Sen Jiang5b372b62016-03-28 16:14:35 -0700219 new_file.reset(new ExtentsFile(std::move(new_file), parsed_new_extents));
220 }
221
222 if (IsOverlapping(old_filename, new_filename, parsed_old_extents,
223 parsed_new_extents)) {
224 // New and old file is overlapping, we can not stream output to new file,
Sen Jiang716d5692016-05-09 16:43:34 -0700225 // cache it in a buffer and write to the file at the end.
Sen Jiang62d5e482017-01-24 16:25:25 -0800226 uint64_t newsize = ParseInt64(patch_data + 24);
Sen Jiang716d5692016-05-09 16:43:34 -0700227 new_file.reset(new BufferFile(std::move(new_file), newsize));
Sen Jiang5b372b62016-03-28 16:14:35 -0700228 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800229
Sen Jiang62d5e482017-01-24 16:25:25 -0800230 return bspatch(old_file, new_file, patch_data, patch_size);
Sen Jiang716d5692016-05-09 16:43:34 -0700231}
232
Sen Jiangb552c792017-01-20 14:07:49 -0800233// Patch |old_data| with |patch_data| and save it by calling sink function.
234// Returns 0 on success, 1 on I/O error and 2 on data error.
Sen Jiang716d5692016-05-09 16:43:34 -0700235int bspatch(const uint8_t* old_data,
236 size_t old_size,
237 const uint8_t* patch_data,
238 size_t patch_size,
239 const sink_func& sink) {
240 std::unique_ptr<FileInterface> old_file(new MemoryFile(old_data, old_size));
241 std::unique_ptr<FileInterface> new_file(new SinkFile(sink));
242
243 return bspatch(old_file, new_file, patch_data, patch_size);
244}
245
Sen Jiangb552c792017-01-20 14:07:49 -0800246// Patch |old_file| with |patch_data| and save it to |new_file|.
247// Returns 0 on success, 1 on I/O error and 2 on data error.
Sen Jiang716d5692016-05-09 16:43:34 -0700248int bspatch(const std::unique_ptr<FileInterface>& old_file,
249 const std::unique_ptr<FileInterface>& new_file,
250 const uint8_t* patch_data,
251 size_t patch_size) {
Tianjie Xu65288122017-10-13 15:10:58 -0700252 BsdiffPatchReader patch_reader;
253 if (!patch_reader.Init(patch_data, patch_size)) {
254 fprintf(stderr, "Failed to initialize patch reader\n");
Sen Jiangb552c792017-01-20 14:07:49 -0800255 return 2;
256 }
Sen Jiang716d5692016-05-09 16:43:34 -0700257
Tianjie Xu65288122017-10-13 15:10:58 -0700258 uint64_t old_file_size;
259 if (!old_file->GetSize(&old_file_size)) {
Sen Jiangb552c792017-01-20 14:07:49 -0800260 fprintf(stderr, "Cannot obtain the size of old file.\n");
261 return 1;
262 }
Sen Jiang716d5692016-05-09 16:43:34 -0700263
Alex Deymo437b7af2015-10-14 20:13:58 -0700264 // The oldpos can be negative, but the new pos is only incremented linearly.
265 int64_t oldpos = 0;
266 uint64_t newpos = 0;
Tianjie Xu65288122017-10-13 15:10:58 -0700267 std::vector<uint8_t> old_buf(1024 * 1024);
268 std::vector<uint8_t> new_buf(1024 * 1024);
269 uint64_t old_file_pos = 0;
270 while (newpos < patch_reader.new_file_size()) {
271 ControlEntry control_entry(0, 0, 0);
272 if (!patch_reader.ParseControlEntry(&control_entry)) {
273 fprintf(stderr, "Failed to read control stream\n");
Sen Jiangb552c792017-01-20 14:07:49 -0800274 return 2;
275 }
Doug Zongker4d054792014-05-13 08:37:06 -0700276
Alex Deymob870eb52015-10-14 21:39:04 -0700277 // Sanity-check.
Tianjie Xu65288122017-10-13 15:10:58 -0700278 if (newpos + control_entry.diff_size > patch_reader.new_file_size()) {
Sen Jiangb552c792017-01-20 14:07:49 -0800279 fprintf(stderr, "Corrupt patch.\n");
280 return 2;
281 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800282
Sen Jiangb552c792017-01-20 14:07:49 -0800283 int ret = 0;
Alex Deymob870eb52015-10-14 21:39:04 -0700284 // Add old data to diff string. It is enough to fseek once, at
285 // the beginning of the sequence, to avoid unnecessary overhead.
Tianjie Xu65288122017-10-13 15:10:58 -0700286 int64_t seek_offset = oldpos;
287 if (seek_offset < 0) {
Sen Jiang5b372b62016-03-28 16:14:35 -0700288 // Write diff block directly to new file without adding old data,
289 // because we will skip part where |oldpos| < 0.
Tianjie Xu65288122017-10-13 15:10:58 -0700290 ret = ReadStreamAndWriteAll(
291 new_file, oldpos - old_file_size, new_buf.data(), new_buf.size(),
292 std::bind(&BsdiffPatchReader::ReadDiffStream, &patch_reader,
293 std::placeholders::_1, std::placeholders::_2));
Sen Jiangb552c792017-01-20 14:07:49 -0800294 if (ret)
295 return ret;
Tianjie Xu65288122017-10-13 15:10:58 -0700296 seek_offset = 0;
Alex Deymob870eb52015-10-14 21:39:04 -0700297 }
Sen Jiang5b372b62016-03-28 16:14:35 -0700298
Tianjie Xu65288122017-10-13 15:10:58 -0700299 // We just checked that |seek_offset| is not negative.
300 if (static_cast<uint64_t>(seek_offset) != old_file_pos &&
301 !old_file->Seek(seek_offset)) {
302 fprintf(stderr, "Error seeking input file to offset %" PRId64 ": %s\n",
303 seek_offset, strerror(errno));
Sen Jiangb552c792017-01-20 14:07:49 -0800304 return 1;
305 }
Alex Deymo437b7af2015-10-14 20:13:58 -0700306
Tianjie Xu65288122017-10-13 15:10:58 -0700307 old_file_pos =
308 std::min<uint64_t>(oldpos + control_entry.diff_size, old_file_size);
309 size_t chunk_size = old_file_pos - seek_offset;
Alex Deymo437b7af2015-10-14 20:13:58 -0700310 while (chunk_size > 0) {
311 size_t read_bytes;
Sen Jiang5b372b62016-03-28 16:14:35 -0700312 size_t bytes_to_read = std::min(chunk_size, old_buf.size());
Sen Jiangb552c792017-01-20 14:07:49 -0800313 if (!old_file->Read(old_buf.data(), bytes_to_read, &read_bytes)) {
314 perror("Error reading from input file");
315 return 1;
316 }
317 if (!read_bytes) {
318 fprintf(stderr, "EOF reached while reading from input file.\n");
319 return 2;
320 }
Sen Jiang5b372b62016-03-28 16:14:35 -0700321 // Read same amount of bytes from diff block
Tianjie Xu65288122017-10-13 15:10:58 -0700322 if (!patch_reader.ReadDiffStream(new_buf.data(), read_bytes)) {
Sen Jiangb552c792017-01-20 14:07:49 -0800323 fprintf(stderr, "Failed to read diff stream.\n");
324 return 2;
325 }
Sen Jiangd87c8352015-11-20 16:14:36 -0800326 // new_buf already has data from diff block, adds old data to it.
327 for (size_t k = 0; k < read_bytes; k++)
Sen Jiang5b372b62016-03-28 16:14:35 -0700328 new_buf[k] += old_buf[k];
Sen Jiangb552c792017-01-20 14:07:49 -0800329 if (!WriteAll(new_file, new_buf.data(), read_bytes)) {
330 perror("Error writing to new file");
331 return 1;
332 }
Alex Deymo437b7af2015-10-14 20:13:58 -0700333 chunk_size -= read_bytes;
Alex Deymob870eb52015-10-14 21:39:04 -0700334 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800335
Alex Deymob870eb52015-10-14 21:39:04 -0700336 // Adjust pointers.
Tianjie Xu65288122017-10-13 15:10:58 -0700337 newpos += control_entry.diff_size;
338 oldpos += control_entry.diff_size;
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800339
Tianjie Xu65288122017-10-13 15:10:58 -0700340 if (oldpos > static_cast<int64_t>(old_file_size)) {
Sen Jiang5b372b62016-03-28 16:14:35 -0700341 // Write diff block directly to new file without adding old data,
Tianjie Xu65288122017-10-13 15:10:58 -0700342 // because we skipped part where |oldpos| > old_file_size.
343 ret = ReadStreamAndWriteAll(
344 new_file, oldpos - old_file_size, new_buf.data(), new_buf.size(),
345 std::bind(&BsdiffPatchReader::ReadDiffStream, &patch_reader,
346 std::placeholders::_1, std::placeholders::_2));
Sen Jiangb552c792017-01-20 14:07:49 -0800347 if (ret)
348 return ret;
Sen Jiang5b372b62016-03-28 16:14:35 -0700349 }
350
Alex Deymob870eb52015-10-14 21:39:04 -0700351 // Sanity-check.
Tianjie Xu65288122017-10-13 15:10:58 -0700352 if (newpos + control_entry.extra_size > patch_reader.new_file_size()) {
Sen Jiangb552c792017-01-20 14:07:49 -0800353 fprintf(stderr, "Corrupt patch.\n");
354 return 2;
355 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800356
Sen Jiang5b372b62016-03-28 16:14:35 -0700357 // Read extra block.
Tianjie Xu65288122017-10-13 15:10:58 -0700358 ret = ReadStreamAndWriteAll(
359 new_file, control_entry.extra_size, new_buf.data(), new_buf.size(),
360 std::bind(&BsdiffPatchReader::ReadExtraStream, &patch_reader,
361 std::placeholders::_1, std::placeholders::_2));
Sen Jiangb552c792017-01-20 14:07:49 -0800362 if (ret)
363 return ret;
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800364
Alex Deymob870eb52015-10-14 21:39:04 -0700365 // Adjust pointers.
Tianjie Xu65288122017-10-13 15:10:58 -0700366 newpos += control_entry.extra_size;
367 oldpos += control_entry.offset_increment;
Sen Jiang5b372b62016-03-28 16:14:35 -0700368 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800369
Alex Deymob870eb52015-10-14 21:39:04 -0700370 // Close input file.
Alex Deymo437b7af2015-10-14 20:13:58 -0700371 old_file->Close();
Gilad Arnold99b53742013-04-30 09:24:14 -0700372
Tianjie Xu65288122017-10-13 15:10:58 -0700373 if (!patch_reader.Finish()) {
374 fprintf(stderr, "Failed to finish the patch reader\n");
375 return 2;
376 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800377
Sen Jiangb552c792017-01-20 14:07:49 -0800378 if (!new_file->Close()) {
379 perror("Error closing new file");
380 return 1;
381 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800382
Alex Deymob870eb52015-10-14 21:39:04 -0700383 return 0;
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800384}
Alex Deymo20891f92015-10-12 17:28:04 -0700385
386} // namespace bsdiff