blob: 550c6c737e30ae7fe5f82d20d565f9812332de31 [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
Gilad Arnold99b53742013-04-30 09:24:14 -070049#include "extents.h"
Alex Deymo437b7af2015-10-14 20:13:58 -070050#include "extents_file.h"
51#include "file.h"
52#include "file_interface.h"
Sen Jiang5b372b62016-03-28 16:14:35 -070053#include "memory_file.h"
Zdenek Behan339e0ee2010-06-14 12:50:53 -070054
Alex Deymo20891f92015-10-12 17:28:04 -070055namespace {
Zdenek Behan339e0ee2010-06-14 12:50:53 -070056
Alex Deymo437b7af2015-10-14 20:13:58 -070057int64_t ParseInt64(u_char* buf) {
58 int64_t y;
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080059
Alex Deymob870eb52015-10-14 21:39:04 -070060 y = buf[7] & 0x7F;
61 y = y * 256;
62 y += buf[6];
63 y = y * 256;
64 y += buf[5];
65 y = y * 256;
66 y += buf[4];
67 y = y * 256;
68 y += buf[3];
69 y = y * 256;
70 y += buf[2];
71 y = y * 256;
72 y += buf[1];
73 y = y * 256;
74 y += buf[0];
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080075
Alex Deymob870eb52015-10-14 21:39:04 -070076 if (buf[7] & 0x80)
77 y = -y;
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080078
Alex Deymob870eb52015-10-14 21:39:04 -070079 return y;
The Android Open Source Projectc285fea2009-03-03 19:29:20 -080080}
81
Sen Jiang5b372b62016-03-28 16:14:35 -070082bool ReadBZ2(BZFILE* pfbz2, uint8_t* data, size_t size) {
83 int bz2err;
84 size_t lenread = BZ2_bzRead(&bz2err, pfbz2, data, size);
85 if (lenread < size || (bz2err != BZ_OK && bz2err != BZ_STREAM_END))
86 return false;
87 return true;
88}
89
90bool ReadBZ2AndWriteAll(const std::unique_ptr<bsdiff::FileInterface>& file,
91 BZFILE* pfbz2,
92 size_t size,
93 uint8_t* buf,
94 size_t buf_size) {
95 while (size > 0) {
96 size_t bytes_to_read = std::min(size, buf_size);
97 if (!ReadBZ2(pfbz2, buf, bytes_to_read))
98 return false;
99 if (!WriteAll(file, buf, bytes_to_read))
100 return false;
101 size -= bytes_to_read;
102 }
103 return true;
104}
105
Alex Deymo20891f92015-10-12 17:28:04 -0700106} // namespace
107
108namespace bsdiff {
109
Sen Jiang5b372b62016-03-28 16:14:35 -0700110bool WriteAll(const std::unique_ptr<FileInterface>& file,
111 const uint8_t* data,
112 size_t size) {
113 size_t offset = 0, written;
114 while (offset < size) {
Sen Jiangb14bb552016-04-11 16:08:03 -0700115 if (!file->Write(data + offset, size - offset, &written) || written == 0)
Sen Jiang5b372b62016-03-28 16:14:35 -0700116 return false;
117 offset += written;
118 }
119 return true;
120}
121
122bool IsOverlapping(const char* old_filename,
123 const char* new_filename,
124 const std::vector<ex_t>& old_extents,
125 const std::vector<ex_t>& new_extents) {
126 struct stat old_stat, new_stat;
127 if (stat(new_filename, &new_stat) == -1) {
128 if (errno == ENOENT)
129 return false;
130 err(1, "Error stat the new filename %s", new_filename);
131 }
132 if (stat(old_filename, &old_stat) == -1)
133 err(1, "Error stat the old filename %s", old_filename);
134
135 if (old_stat.st_dev != new_stat.st_dev || old_stat.st_ino != new_stat.st_ino)
136 return false;
137
138 if (old_extents.empty() && new_extents.empty())
139 return true;
140
141 for (ex_t old_ex : old_extents)
142 for (ex_t new_ex : new_extents)
143 if (static_cast<uint64_t>(old_ex.off) < new_ex.off + new_ex.len &&
144 static_cast<uint64_t>(new_ex.off) < old_ex.off + old_ex.len)
145 return true;
146
147 return false;
148}
149
Alex Deymoa5cff222015-04-08 14:10:30 -0700150int bspatch(
151 const char* old_filename, const char* new_filename,
152 const char* patch_filename,
153 const char* old_extents, const char* new_extents) {
Alex Deymob870eb52015-10-14 21:39:04 -0700154 FILE* f, *cpf, *dpf, *epf;
155 BZFILE* cpfbz2, *dpfbz2, *epfbz2;
Sen Jiang5b372b62016-03-28 16:14:35 -0700156 int bz2err;
Alex Deymob870eb52015-10-14 21:39:04 -0700157 ssize_t bzctrllen, bzdatalen;
158 u_char header[32], buf[8];
Alex Deymob870eb52015-10-14 21:39:04 -0700159 off_t ctrl[3];
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800160
Alex Deymob870eb52015-10-14 21:39:04 -0700161 int using_extents = (old_extents != NULL || new_extents != NULL);
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800162
Alex Deymob870eb52015-10-14 21:39:04 -0700163 // Open patch file.
164 if ((f = fopen(patch_filename, "r")) == NULL)
165 err(1, "fopen(%s)", patch_filename);
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800166
Alex Deymob870eb52015-10-14 21:39:04 -0700167 // File format:
168 // 0 8 "BSDIFF40"
169 // 8 8 X
170 // 16 8 Y
171 // 24 8 sizeof(new_filename)
172 // 32 X bzip2(control block)
173 // 32+X Y bzip2(diff block)
174 // 32+X+Y ??? bzip2(extra block)
175 // with control block a set of triples (x,y,z) meaning "add x bytes
176 // from oldfile to x bytes from the diff block; copy y bytes from the
177 // extra block; seek forwards in oldfile by z bytes".
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800178
Alex Deymob870eb52015-10-14 21:39:04 -0700179 // Read header.
180 if (fread(header, 1, 32, f) < 32) {
181 if (feof(f))
182 errx(1, "Corrupt patch\n");
183 err(1, "fread(%s)", patch_filename);
184 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800185
Alex Deymob870eb52015-10-14 21:39:04 -0700186 // Check for appropriate magic.
187 if (memcmp(header, "BSDIFF40", 8) != 0)
188 errx(1, "Corrupt patch\n");
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800189
Alex Deymob870eb52015-10-14 21:39:04 -0700190 // Read lengths from header.
Alex Deymo437b7af2015-10-14 20:13:58 -0700191 uint64_t oldsize, newsize;
192 bzctrllen = ParseInt64(header + 8);
193 bzdatalen = ParseInt64(header + 16);
194 int64_t signed_newsize = ParseInt64(header + 24);
195 newsize = signed_newsize;
196 if ((bzctrllen < 0) || (bzdatalen < 0) || (signed_newsize < 0))
Alex Deymob870eb52015-10-14 21:39:04 -0700197 errx(1, "Corrupt patch\n");
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800198
Alex Deymob870eb52015-10-14 21:39:04 -0700199 // Close patch file and re-open it via libbzip2 at the right places.
200 if (fclose(f))
201 err(1, "fclose(%s)", patch_filename);
202 if ((cpf = fopen(patch_filename, "r")) == NULL)
203 err(1, "fopen(%s)", patch_filename);
204 if (fseek(cpf, 32, SEEK_SET))
205 err(1, "fseeko(%s, %lld)", patch_filename, (long long)32);
Sen Jiang5b372b62016-03-28 16:14:35 -0700206 if ((cpfbz2 = BZ2_bzReadOpen(&bz2err, cpf, 0, 0, NULL, 0)) == NULL)
207 errx(1, "BZ2_bzReadOpen, bz2err = %d", bz2err);
Alex Deymob870eb52015-10-14 21:39:04 -0700208 if ((dpf = fopen(patch_filename, "r")) == NULL)
209 err(1, "fopen(%s)", patch_filename);
210 if (fseek(dpf, 32 + bzctrllen, SEEK_SET))
211 err(1, "fseeko(%s, %lld)", patch_filename, (long long)(32 + bzctrllen));
Sen Jiang5b372b62016-03-28 16:14:35 -0700212 if ((dpfbz2 = BZ2_bzReadOpen(&bz2err, dpf, 0, 0, NULL, 0)) == NULL)
213 errx(1, "BZ2_bzReadOpen, bz2err = %d", bz2err);
Alex Deymob870eb52015-10-14 21:39:04 -0700214 if ((epf = fopen(patch_filename, "r")) == NULL)
215 err(1, "fopen(%s)", patch_filename);
216 if (fseek(epf, 32 + bzctrllen + bzdatalen, SEEK_SET))
217 err(1, "fseeko(%s, %lld)", patch_filename,
218 (long long)(32 + bzctrllen + bzdatalen));
Sen Jiang5b372b62016-03-28 16:14:35 -0700219 if ((epfbz2 = BZ2_bzReadOpen(&bz2err, epf, 0, 0, NULL, 0)) == NULL)
220 errx(1, "BZ2_bzReadOpen, bz2err = %d", bz2err);
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800221
Alex Deymob870eb52015-10-14 21:39:04 -0700222 // Open input file for reading.
Alex Deymo437b7af2015-10-14 20:13:58 -0700223 std::unique_ptr<FileInterface> old_file = File::FOpen(old_filename, O_RDONLY);
224 if (!old_file)
225 err(1, "Error opening the old filename");
226
Sen Jiang5b372b62016-03-28 16:14:35 -0700227 std::vector<ex_t> parsed_old_extents;
Alex Deymob870eb52015-10-14 21:39:04 -0700228 if (using_extents) {
Alex Deymo437b7af2015-10-14 20:13:58 -0700229 if (!ParseExtentStr(old_extents, &parsed_old_extents))
230 errx(1, "Error parsing the old extents");
231 old_file.reset(new ExtentsFile(std::move(old_file), parsed_old_extents));
232 }
233
234 if (!old_file->GetSize(&oldsize))
Alex Deymob870eb52015-10-14 21:39:04 -0700235 err(1, "cannot obtain the size of %s", old_filename);
Alex Deymo437b7af2015-10-14 20:13:58 -0700236 uint64_t old_file_pos = 0;
Gilad Arnold99b53742013-04-30 09:24:14 -0700237
Sen Jiang5b372b62016-03-28 16:14:35 -0700238 // Open output file for writing.
239 std::unique_ptr<FileInterface> new_file =
240 File::FOpen(new_filename, O_CREAT | O_WRONLY);
241 if (!new_file)
242 err(1, "Error opening the new filename %s", new_filename);
243
244 std::vector<ex_t> parsed_new_extents;
245 if (using_extents) {
246 if (!ParseExtentStr(new_extents, &parsed_new_extents))
247 errx(1, "Error parsing the new extents");
248 new_file.reset(new ExtentsFile(std::move(new_file), parsed_new_extents));
249 }
250
251 if (IsOverlapping(old_filename, new_filename, parsed_old_extents,
252 parsed_new_extents)) {
253 // New and old file is overlapping, we can not stream output to new file,
254 // cache it in the memory and write to the file at the end.
255 new_file.reset(new MemoryFile(std::move(new_file), newsize));
256 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800257
Alex Deymo437b7af2015-10-14 20:13:58 -0700258 // The oldpos can be negative, but the new pos is only incremented linearly.
259 int64_t oldpos = 0;
260 uint64_t newpos = 0;
Sen Jiang5b372b62016-03-28 16:14:35 -0700261 std::vector<uint8_t> old_buf(1024 * 1024), new_buf(1024 * 1024);
Alex Deymob870eb52015-10-14 21:39:04 -0700262 while (newpos < newsize) {
Sen Jiang5b372b62016-03-28 16:14:35 -0700263 int64_t i;
Alex Deymob870eb52015-10-14 21:39:04 -0700264 // Read control data.
265 for (i = 0; i <= 2; i++) {
Sen Jiang5b372b62016-03-28 16:14:35 -0700266 if (!ReadBZ2(cpfbz2, buf, 8))
Alex Deymob870eb52015-10-14 21:39:04 -0700267 errx(1, "Corrupt patch\n");
Alex Deymo437b7af2015-10-14 20:13:58 -0700268 ctrl[i] = ParseInt64(buf);
Sen Jiang5b372b62016-03-28 16:14:35 -0700269 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800270
Alex Deymob870eb52015-10-14 21:39:04 -0700271 // Sanity-check.
272 if (ctrl[0] < 0 || ctrl[1] < 0)
273 errx(1, "Corrupt patch\n");
Doug Zongker4d054792014-05-13 08:37:06 -0700274
Alex Deymob870eb52015-10-14 21:39:04 -0700275 // Sanity-check.
276 if (newpos + ctrl[0] > newsize)
277 errx(1, "Corrupt patch\n");
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800278
Alex Deymob870eb52015-10-14 21:39:04 -0700279 // Add old data to diff string. It is enough to fseek once, at
280 // the beginning of the sequence, to avoid unnecessary overhead.
Alex Deymob870eb52015-10-14 21:39:04 -0700281 if ((i = oldpos) < 0) {
Sen Jiang5b372b62016-03-28 16:14:35 -0700282 // Write diff block directly to new file without adding old data,
283 // because we will skip part where |oldpos| < 0.
284 if (!ReadBZ2AndWriteAll(new_file, dpfbz2, -i, new_buf.data(),
285 new_buf.size()))
286 errx(1, "Error during ReadBZ2AndWriteAll()");
287
Alex Deymob870eb52015-10-14 21:39:04 -0700288 i = 0;
289 }
Sen Jiang5b372b62016-03-28 16:14:35 -0700290
Alex Deymo437b7af2015-10-14 20:13:58 -0700291 // We just checked that |i| is not negative.
292 if (static_cast<uint64_t>(i) != old_file_pos && !old_file->Seek(i))
293 err(1, "error seeking input file to offset %" PRId64, i);
Alex Deymob870eb52015-10-14 21:39:04 -0700294 if ((old_file_pos = oldpos + ctrl[0]) > oldsize)
295 old_file_pos = oldsize;
Alex Deymo437b7af2015-10-14 20:13:58 -0700296
Sen Jiang5b372b62016-03-28 16:14:35 -0700297 size_t chunk_size = old_file_pos - i;
Alex Deymo437b7af2015-10-14 20:13:58 -0700298 while (chunk_size > 0) {
299 size_t read_bytes;
Sen Jiang5b372b62016-03-28 16:14:35 -0700300 size_t bytes_to_read = std::min(chunk_size, old_buf.size());
Sen Jiangd87c8352015-11-20 16:14:36 -0800301 if (!old_file->Read(old_buf.data(), bytes_to_read, &read_bytes))
Alex Deymob870eb52015-10-14 21:39:04 -0700302 err(1, "error reading from input file");
Alex Deymo437b7af2015-10-14 20:13:58 -0700303 if (!read_bytes)
304 errx(1, "EOF reached while reading from input file");
Sen Jiang5b372b62016-03-28 16:14:35 -0700305 // Read same amount of bytes from diff block
306 if (!ReadBZ2(dpfbz2, new_buf.data(), read_bytes))
307 errx(1, "Corrupt patch\n");
Sen Jiangd87c8352015-11-20 16:14:36 -0800308 // new_buf already has data from diff block, adds old data to it.
309 for (size_t k = 0; k < read_bytes; k++)
Sen Jiang5b372b62016-03-28 16:14:35 -0700310 new_buf[k] += old_buf[k];
311 if (!WriteAll(new_file, new_buf.data(), read_bytes))
312 err(1, "Error writing new file.");
Alex Deymo437b7af2015-10-14 20:13:58 -0700313 chunk_size -= read_bytes;
Alex Deymob870eb52015-10-14 21:39:04 -0700314 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800315
Alex Deymob870eb52015-10-14 21:39:04 -0700316 // Adjust pointers.
317 newpos += ctrl[0];
318 oldpos += ctrl[0];
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800319
Sen Jiang5b372b62016-03-28 16:14:35 -0700320 if (oldpos > static_cast<int64_t>(oldsize)) {
321 // Write diff block directly to new file without adding old data,
322 // because we skipped part where |oldpos| > oldsize.
323 if (!ReadBZ2AndWriteAll(new_file, dpfbz2, oldpos - oldsize,
324 new_buf.data(), new_buf.size()))
325 errx(1, "Error during ReadBZ2AndWriteAll()");
326 }
327
Alex Deymob870eb52015-10-14 21:39:04 -0700328 // Sanity-check.
329 if (newpos + ctrl[1] > newsize)
330 errx(1, "Corrupt patch\n");
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800331
Sen Jiang5b372b62016-03-28 16:14:35 -0700332 // Read extra block.
333 if (!ReadBZ2AndWriteAll(new_file, epfbz2, ctrl[1], new_buf.data(),
334 new_buf.size()))
335 errx(1, "Error during ReadBZ2AndWriteAll()");
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800336
Alex Deymob870eb52015-10-14 21:39:04 -0700337 // Adjust pointers.
338 newpos += ctrl[1];
339 oldpos += ctrl[2];
Sen Jiang5b372b62016-03-28 16:14:35 -0700340 }
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800341
Alex Deymob870eb52015-10-14 21:39:04 -0700342 // Close input file.
Alex Deymo437b7af2015-10-14 20:13:58 -0700343 old_file->Close();
Gilad Arnold99b53742013-04-30 09:24:14 -0700344
Alex Deymob870eb52015-10-14 21:39:04 -0700345 // Clean up the bzip2 reads.
Sen Jiang5b372b62016-03-28 16:14:35 -0700346 BZ2_bzReadClose(&bz2err, cpfbz2);
347 BZ2_bzReadClose(&bz2err, dpfbz2);
348 BZ2_bzReadClose(&bz2err, epfbz2);
Alex Deymob870eb52015-10-14 21:39:04 -0700349 if (fclose(cpf) || fclose(dpf) || fclose(epf))
350 err(1, "fclose(%s)", patch_filename);
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800351
Alex Deymo437b7af2015-10-14 20:13:58 -0700352 if (!new_file->Close())
353 err(1, "Error closing new file %s", new_filename);
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800354
Alex Deymob870eb52015-10-14 21:39:04 -0700355 return 0;
The Android Open Source Projectc285fea2009-03-03 19:29:20 -0800356}
Alex Deymo20891f92015-10-12 17:28:04 -0700357
358} // namespace bsdiff