blob: 693213a596e96705f9bec3e3a5360e9ff16218a8 [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 Deymob870eb52015-10-14 21:39:04 -070031#include "bspatch.h"
32
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080033#include <bzlib.h>
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080034#include <err.h>
Sen Jiang5b372b62016-03-28 16:14:35 -070035#include <errno.h>
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080036#include <fcntl.h>
Gilad Arnold99b53742013-04-30 09:24:14 -070037#include <inttypes.h>
Gilad Arnold99b53742013-04-30 09:24:14 -070038#include <stdlib.h>
39#include <string.h>
40#include <unistd.h>
Sen Jiang5b372b62016-03-28 16:14:35 -070041#include <sys/stat.h>
Alex Deymob870eb52015-10-14 21:39:04 -070042#include <sys/types.h>
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080043
Alex Deymo437b7af2015-10-14 20:13:58 -070044#include <algorithm>
45#include <memory>
46#include <limits>
Sen Jiang5b372b62016-03-28 16:14:35 -070047#include <vector>
Alex Deymo437b7af2015-10-14 20:13:58 -070048
Sen Jiang716d5692016-05-09 16:43:34 -070049#include "buffer_file.h"
Gilad Arnold99b53742013-04-30 09:24:14 -070050#include "extents.h"
Alex Deymo437b7af2015-10-14 20:13:58 -070051#include "extents_file.h"
52#include "file.h"
53#include "file_interface.h"
Sen Jiang5b372b62016-03-28 16:14:35 -070054#include "memory_file.h"
Sen Jiang716d5692016-05-09 16:43:34 -070055#include "sink_file.h"
Zdenek Behan339e0ee2010-06-14 12:50:53 -070056
Alex Deymo20891f92015-10-12 17:28:04 -070057namespace {
Zdenek Behan339e0ee2010-06-14 12:50:53 -070058
Sen Jiang716d5692016-05-09 16:43:34 -070059int64_t ParseInt64(const u_char* buf) {
Alex Deymo437b7af2015-10-14 20:13:58 -070060 int64_t y;
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080061
Alex Deymob870eb52015-10-14 21:39:04 -070062 y = buf[7] & 0x7F;
63 y = y * 256;
64 y += buf[6];
65 y = y * 256;
66 y += buf[5];
67 y = y * 256;
68 y += buf[4];
69 y = y * 256;
70 y += buf[3];
71 y = y * 256;
72 y += buf[2];
73 y = y * 256;
74 y += buf[1];
75 y = y * 256;
76 y += buf[0];
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080077
Alex Deymob870eb52015-10-14 21:39:04 -070078 if (buf[7] & 0x80)
79 y = -y;
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080080
Alex Deymob870eb52015-10-14 21:39:04 -070081 return y;
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080082}
83
Sen Jiang716d5692016-05-09 16:43:34 -070084bool ReadBZ2(bz_stream* stream, uint8_t* data, size_t size) {
85 stream->next_out = (char*)data;
86 while (size > 0) {
87 unsigned int read_size = std::min(
88 static_cast<size_t>(std::numeric_limits<unsigned int>::max()), size);
89 stream->avail_out = read_size;
90 int bz2err = BZ2_bzDecompress(stream);
91 if (bz2err != BZ_OK && bz2err != BZ_STREAM_END)
92 return false;
93 size -= read_size - stream->avail_out;
94 }
Sen Jiang5b372b62016-03-28 16:14:35 -070095 return true;
96}
97
98bool ReadBZ2AndWriteAll(const std::unique_ptr<bsdiff::FileInterface>& file,
Sen Jiang716d5692016-05-09 16:43:34 -070099 bz_stream* stream,
Sen Jiang5b372b62016-03-28 16:14:35 -0700100 size_t size,
101 uint8_t* buf,
102 size_t buf_size) {
103 while (size > 0) {
104 size_t bytes_to_read = std::min(size, buf_size);
Sen Jiang716d5692016-05-09 16:43:34 -0700105 if (!ReadBZ2(stream, buf, bytes_to_read))
Sen Jiang5b372b62016-03-28 16:14:35 -0700106 return false;
107 if (!WriteAll(file, buf, bytes_to_read))
108 return false;
109 size -= bytes_to_read;
110 }
111 return true;
112}
113
Alex Deymo20891f92015-10-12 17:28:04 -0700114} // namespace
115
116namespace bsdiff {
117
Sen Jiang716d5692016-05-09 16:43:34 -0700118bool ReadAll(const std::unique_ptr<FileInterface>& file,
119 uint8_t* data,
120 size_t size) {
121 size_t offset = 0, read;
122 while (offset < size) {
123 if (!file->Read(data + offset, size - offset, &read) || read == 0)
124 return false;
125 offset += read;
126 }
127 return true;
128}
129
Sen Jiang5b372b62016-03-28 16:14:35 -0700130bool WriteAll(const std::unique_ptr<FileInterface>& file,
131 const uint8_t* data,
132 size_t size) {
133 size_t offset = 0, written;
134 while (offset < size) {
Sen Jiangb14bb552016-04-11 16:08:03 -0700135 if (!file->Write(data + offset, size - offset, &written) || written == 0)
Sen Jiang5b372b62016-03-28 16:14:35 -0700136 return false;
137 offset += written;
138 }
139 return true;
140}
141
142bool IsOverlapping(const char* old_filename,
143 const char* new_filename,
144 const std::vector<ex_t>& old_extents,
145 const std::vector<ex_t>& new_extents) {
146 struct stat old_stat, new_stat;
147 if (stat(new_filename, &new_stat) == -1) {
148 if (errno == ENOENT)
149 return false;
150 err(1, "Error stat the new filename %s", new_filename);
151 }
152 if (stat(old_filename, &old_stat) == -1)
153 err(1, "Error stat the old filename %s", old_filename);
154
155 if (old_stat.st_dev != new_stat.st_dev || old_stat.st_ino != new_stat.st_ino)
156 return false;
157
158 if (old_extents.empty() && new_extents.empty())
159 return true;
160
161 for (ex_t old_ex : old_extents)
162 for (ex_t new_ex : new_extents)
163 if (static_cast<uint64_t>(old_ex.off) < new_ex.off + new_ex.len &&
164 static_cast<uint64_t>(new_ex.off) < old_ex.off + old_ex.len)
165 return true;
166
167 return false;
168}
169
Alex Deymoa5cff222015-04-08 14:10:30 -0700170int bspatch(
171 const char* old_filename, const char* new_filename,
172 const char* patch_filename,
173 const char* old_extents, const char* new_extents) {
Sen Jiang716d5692016-05-09 16:43:34 -0700174 std::unique_ptr<FileInterface> patch_file =
175 File::FOpen(patch_filename, O_RDONLY);
176 if (!patch_file)
177 err(1, "Error opening the patch filename %s", patch_filename);
178 uint64_t patch_size;
179 patch_file->GetSize(&patch_size);
180 std::vector<uint8_t> patch(patch_size);
181 if (!ReadAll(patch_file, patch.data(), patch_size))
182 errx(1, "Corrupt patch\n");
183 patch_file.reset();
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800184
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);
189 if (!old_file)
Sen Jiang716d5692016-05-09 16:43:34 -0700190 err(1, "Error opening the old filename %s", old_filename);
Alex Deymo437b7af2015-10-14 20:13:58 -0700191
Sen Jiang5b372b62016-03-28 16:14:35 -0700192 std::vector<ex_t> parsed_old_extents;
Alex Deymob870eb52015-10-14 21:39:04 -0700193 if (using_extents) {
Alex Deymo437b7af2015-10-14 20:13:58 -0700194 if (!ParseExtentStr(old_extents, &parsed_old_extents))
195 errx(1, "Error parsing the old extents");
196 old_file.reset(new ExtentsFile(std::move(old_file), parsed_old_extents));
197 }
198
Sen Jiang5b372b62016-03-28 16:14:35 -0700199 // Open output file for writing.
200 std::unique_ptr<FileInterface> new_file =
201 File::FOpen(new_filename, O_CREAT | O_WRONLY);
202 if (!new_file)
203 err(1, "Error opening the new filename %s", new_filename);
204
205 std::vector<ex_t> parsed_new_extents;
206 if (using_extents) {
207 if (!ParseExtentStr(new_extents, &parsed_new_extents))
208 errx(1, "Error parsing the new extents");
209 new_file.reset(new ExtentsFile(std::move(new_file), parsed_new_extents));
210 }
211
212 if (IsOverlapping(old_filename, new_filename, parsed_old_extents,
213 parsed_new_extents)) {
214 // New and old file is overlapping, we can not stream output to new file,
Sen Jiang716d5692016-05-09 16:43:34 -0700215 // cache it in a buffer and write to the file at the end.
216 uint64_t newsize = ParseInt64(patch.data() + 24);
217 new_file.reset(new BufferFile(std::move(new_file), newsize));
Sen Jiang5b372b62016-03-28 16:14:35 -0700218 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800219
Sen Jiang716d5692016-05-09 16:43:34 -0700220 return bspatch(old_file, new_file, patch.data(), patch_size);
221}
222
223int bspatch(const uint8_t* old_data,
224 size_t old_size,
225 const uint8_t* patch_data,
226 size_t patch_size,
227 const sink_func& sink) {
228 std::unique_ptr<FileInterface> old_file(new MemoryFile(old_data, old_size));
229 std::unique_ptr<FileInterface> new_file(new SinkFile(sink));
230
231 return bspatch(old_file, new_file, patch_data, patch_size);
232}
233
234int bspatch(const std::unique_ptr<FileInterface>& old_file,
235 const std::unique_ptr<FileInterface>& new_file,
236 const uint8_t* patch_data,
237 size_t patch_size) {
238 int bz2err;
239 u_char buf[8];
240 off_t ctrl[3];
241
242 // File format:
243 // 0 8 "BSDIFF40"
244 // 8 8 X
245 // 16 8 Y
246 // 24 8 sizeof(new_filename)
247 // 32 X bzip2(control block)
248 // 32+X Y bzip2(diff block)
249 // 32+X+Y ??? bzip2(extra block)
250 // with control block a set of triples (x,y,z) meaning "add x bytes
251 // from oldfile to x bytes from the diff block; copy y bytes from the
252 // extra block; seek forwards in oldfile by z bytes".
253
254 // Check for appropriate magic.
255 if (memcmp(patch_data, "BSDIFF40", 8) != 0)
256 errx(1, "Corrupt patch\n");
257
258 // Read lengths from header.
259 uint64_t oldsize, newsize;
260 int64_t ctrl_len = ParseInt64(patch_data + 8);
261 int64_t data_len = ParseInt64(patch_data + 16);
262 int64_t signed_newsize = ParseInt64(patch_data + 24);
263 newsize = signed_newsize;
264 if ((ctrl_len < 0) || (data_len < 0) || (signed_newsize < 0) ||
265 (32 + ctrl_len + data_len > static_cast<int64_t>(patch_size)))
266 errx(1, "Corrupt patch\n");
267
268 bz_stream cstream;
269 cstream.next_in = (char*)patch_data + 32;
270 cstream.avail_in = ctrl_len;
271 cstream.bzalloc = nullptr;
272 cstream.bzfree = nullptr;
273 cstream.opaque = nullptr;
274 if ((bz2err = BZ2_bzDecompressInit(&cstream, 0, 0)) != BZ_OK)
275 errx(1, "failed to bzinit control stream (%d)\n", bz2err);
276
277 bz_stream dstream;
278 dstream.next_in = (char*)patch_data + 32 + ctrl_len;
279 dstream.avail_in = data_len;
280 dstream.bzalloc = nullptr;
281 dstream.bzfree = nullptr;
282 dstream.opaque = nullptr;
283 if ((bz2err = BZ2_bzDecompressInit(&dstream, 0, 0)) != BZ_OK)
284 errx(1, "failed to bzinit diff stream (%d)\n", bz2err);
285
286 bz_stream estream;
287 estream.next_in = (char*)patch_data + 32 + ctrl_len + data_len;
288 estream.avail_in = patch_size - (32 + ctrl_len + data_len);
289 estream.bzalloc = nullptr;
290 estream.bzfree = nullptr;
291 estream.opaque = nullptr;
292 if ((bz2err = BZ2_bzDecompressInit(&estream, 0, 0)) != BZ_OK)
293 errx(1, "failed to bzinit extra stream (%d)\n", bz2err);
294
295 uint64_t old_file_pos = 0;
296
297 if (!old_file->GetSize(&oldsize))
298 err(1, "cannot obtain the size of old file");
299
Alex Deymo437b7af2015-10-14 20:13:58 -0700300 // The oldpos can be negative, but the new pos is only incremented linearly.
301 int64_t oldpos = 0;
302 uint64_t newpos = 0;
Sen Jiang5b372b62016-03-28 16:14:35 -0700303 std::vector<uint8_t> old_buf(1024 * 1024), new_buf(1024 * 1024);
Alex Deymob870eb52015-10-14 21:39:04 -0700304 while (newpos < newsize) {
Sen Jiang5b372b62016-03-28 16:14:35 -0700305 int64_t i;
Alex Deymob870eb52015-10-14 21:39:04 -0700306 // Read control data.
307 for (i = 0; i <= 2; i++) {
Sen Jiang716d5692016-05-09 16:43:34 -0700308 if (!ReadBZ2(&cstream, buf, 8))
Alex Deymob870eb52015-10-14 21:39:04 -0700309 errx(1, "Corrupt patch\n");
Alex Deymo437b7af2015-10-14 20:13:58 -0700310 ctrl[i] = ParseInt64(buf);
Sen Jiang5b372b62016-03-28 16:14:35 -0700311 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800312
Alex Deymob870eb52015-10-14 21:39:04 -0700313 // Sanity-check.
314 if (ctrl[0] < 0 || ctrl[1] < 0)
315 errx(1, "Corrupt patch\n");
Doug Zongker4d054792014-05-13 08:37:06 -0700316
Alex Deymob870eb52015-10-14 21:39:04 -0700317 // Sanity-check.
318 if (newpos + ctrl[0] > newsize)
319 errx(1, "Corrupt patch\n");
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800320
Alex Deymob870eb52015-10-14 21:39:04 -0700321 // Add old data to diff string. It is enough to fseek once, at
322 // the beginning of the sequence, to avoid unnecessary overhead.
Alex Deymob870eb52015-10-14 21:39:04 -0700323 if ((i = oldpos) < 0) {
Sen Jiang5b372b62016-03-28 16:14:35 -0700324 // Write diff block directly to new file without adding old data,
325 // because we will skip part where |oldpos| < 0.
Sen Jiang716d5692016-05-09 16:43:34 -0700326 if (!ReadBZ2AndWriteAll(new_file, &dstream, -i, new_buf.data(),
Sen Jiang5b372b62016-03-28 16:14:35 -0700327 new_buf.size()))
328 errx(1, "Error during ReadBZ2AndWriteAll()");
329
Alex Deymob870eb52015-10-14 21:39:04 -0700330 i = 0;
331 }
Sen Jiang5b372b62016-03-28 16:14:35 -0700332
Alex Deymo437b7af2015-10-14 20:13:58 -0700333 // We just checked that |i| is not negative.
334 if (static_cast<uint64_t>(i) != old_file_pos && !old_file->Seek(i))
335 err(1, "error seeking input file to offset %" PRId64, i);
Alex Deymob870eb52015-10-14 21:39:04 -0700336 if ((old_file_pos = oldpos + ctrl[0]) > oldsize)
337 old_file_pos = oldsize;
Alex Deymo437b7af2015-10-14 20:13:58 -0700338
Sen Jiang5b372b62016-03-28 16:14:35 -0700339 size_t chunk_size = old_file_pos - i;
Alex Deymo437b7af2015-10-14 20:13:58 -0700340 while (chunk_size > 0) {
341 size_t read_bytes;
Sen Jiang5b372b62016-03-28 16:14:35 -0700342 size_t bytes_to_read = std::min(chunk_size, old_buf.size());
Sen Jiangd87c8352015-11-20 16:14:36 -0800343 if (!old_file->Read(old_buf.data(), bytes_to_read, &read_bytes))
Alex Deymob870eb52015-10-14 21:39:04 -0700344 err(1, "error reading from input file");
Alex Deymo437b7af2015-10-14 20:13:58 -0700345 if (!read_bytes)
346 errx(1, "EOF reached while reading from input file");
Sen Jiang5b372b62016-03-28 16:14:35 -0700347 // Read same amount of bytes from diff block
Sen Jiang716d5692016-05-09 16:43:34 -0700348 if (!ReadBZ2(&dstream, new_buf.data(), read_bytes))
Sen Jiang5b372b62016-03-28 16:14:35 -0700349 errx(1, "Corrupt patch\n");
Sen Jiangd87c8352015-11-20 16:14:36 -0800350 // new_buf already has data from diff block, adds old data to it.
351 for (size_t k = 0; k < read_bytes; k++)
Sen Jiang5b372b62016-03-28 16:14:35 -0700352 new_buf[k] += old_buf[k];
353 if (!WriteAll(new_file, new_buf.data(), read_bytes))
354 err(1, "Error writing new file.");
Alex Deymo437b7af2015-10-14 20:13:58 -0700355 chunk_size -= read_bytes;
Alex Deymob870eb52015-10-14 21:39:04 -0700356 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800357
Alex Deymob870eb52015-10-14 21:39:04 -0700358 // Adjust pointers.
359 newpos += ctrl[0];
360 oldpos += ctrl[0];
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800361
Sen Jiang5b372b62016-03-28 16:14:35 -0700362 if (oldpos > static_cast<int64_t>(oldsize)) {
363 // Write diff block directly to new file without adding old data,
364 // because we skipped part where |oldpos| > oldsize.
Sen Jiang716d5692016-05-09 16:43:34 -0700365 if (!ReadBZ2AndWriteAll(new_file, &dstream, oldpos - oldsize,
Sen Jiang5b372b62016-03-28 16:14:35 -0700366 new_buf.data(), new_buf.size()))
367 errx(1, "Error during ReadBZ2AndWriteAll()");
368 }
369
Alex Deymob870eb52015-10-14 21:39:04 -0700370 // Sanity-check.
371 if (newpos + ctrl[1] > newsize)
372 errx(1, "Corrupt patch\n");
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800373
Sen Jiang5b372b62016-03-28 16:14:35 -0700374 // Read extra block.
Sen Jiang716d5692016-05-09 16:43:34 -0700375 if (!ReadBZ2AndWriteAll(new_file, &estream, ctrl[1], new_buf.data(),
Sen Jiang5b372b62016-03-28 16:14:35 -0700376 new_buf.size()))
377 errx(1, "Error during ReadBZ2AndWriteAll()");
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800378
Alex Deymob870eb52015-10-14 21:39:04 -0700379 // Adjust pointers.
380 newpos += ctrl[1];
381 oldpos += ctrl[2];
Sen Jiang5b372b62016-03-28 16:14:35 -0700382 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800383
Alex Deymob870eb52015-10-14 21:39:04 -0700384 // Close input file.
Alex Deymo437b7af2015-10-14 20:13:58 -0700385 old_file->Close();
Gilad Arnold99b53742013-04-30 09:24:14 -0700386
Alex Deymob870eb52015-10-14 21:39:04 -0700387 // Clean up the bzip2 reads.
Sen Jiang716d5692016-05-09 16:43:34 -0700388 BZ2_bzDecompressEnd(&cstream);
389 BZ2_bzDecompressEnd(&dstream);
390 BZ2_bzDecompressEnd(&estream);
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800391
Alex Deymo437b7af2015-10-14 20:13:58 -0700392 if (!new_file->Close())
Sen Jiang716d5692016-05-09 16:43:34 -0700393 err(1, "Error closing new file");
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800394
Alex Deymob870eb52015-10-14 21:39:04 -0700395 return 0;
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800396}
Alex Deymo20891f92015-10-12 17:28:04 -0700397
398} // namespace bsdiff