1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// This file is a nearly line-for-line copy of bspatch.c from the
18// bsdiff-4.3 distribution; the primary differences being how the
19// input and output data are read and the error handling.  Running
20// applypatch with the -l option will display the bsdiff license
21// notice.
22
23#include <stdio.h>
24#include <sys/stat.h>
25#include <errno.h>
26#include <unistd.h>
27#include <string.h>
28
29#include <bzlib.h>
30
31#include "mincrypt/sha.h"
32#include "applypatch.h"
33
34void ShowBSDiffLicense() {
35    puts("The bsdiff library used herein is:\n"
36         "\n"
37         "Copyright 2003-2005 Colin Percival\n"
38         "All rights reserved\n"
39         "\n"
40         "Redistribution and use in source and binary forms, with or without\n"
41         "modification, are permitted providing that the following conditions\n"
42         "are met:\n"
43         "1. Redistributions of source code must retain the above copyright\n"
44         "   notice, this list of conditions and the following disclaimer.\n"
45         "2. Redistributions in binary form must reproduce the above copyright\n"
46         "   notice, this list of conditions and the following disclaimer in the\n"
47         "   documentation and/or other materials provided with the distribution.\n"
48         "\n"
49         "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
50         "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
51         "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
52         "ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n"
53         "DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n"
54         "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n"
55         "OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
56         "HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n"
57         "STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n"
58         "IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
59         "POSSIBILITY OF SUCH DAMAGE.\n"
60         "\n------------------\n\n"
61         "This program uses Julian R Seward's \"libbzip2\" library, available\n"
62         "from http://www.bzip.org/.\n"
63        );
64}
65
66static off_t offtin(u_char *buf)
67{
68    off_t y;
69
70    y=buf[7]&0x7F;
71    y=y*256;y+=buf[6];
72    y=y*256;y+=buf[5];
73    y=y*256;y+=buf[4];
74    y=y*256;y+=buf[3];
75    y=y*256;y+=buf[2];
76    y=y*256;y+=buf[1];
77    y=y*256;y+=buf[0];
78
79    if(buf[7]&0x80) y=-y;
80
81    return y;
82}
83
84int FillBuffer(unsigned char* buffer, int size, bz_stream* stream) {
85    stream->next_out = (char*)buffer;
86    stream->avail_out = size;
87    while (stream->avail_out > 0) {
88        int bzerr = BZ2_bzDecompress(stream);
89        if (bzerr != BZ_OK && bzerr != BZ_STREAM_END) {
90            printf("bz error %d decompressing\n", bzerr);
91            return -1;
92        }
93        if (stream->avail_out > 0) {
94            printf("need %d more bytes\n", stream->avail_out);
95        }
96    }
97    return 0;
98}
99
100int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
101                     const Value* patch, ssize_t patch_offset,
102                     SinkFn sink, void* token, SHA_CTX* ctx) {
103
104    unsigned char* new_data;
105    ssize_t new_size;
106    if (ApplyBSDiffPatchMem(old_data, old_size, patch, patch_offset,
107                            &new_data, &new_size) != 0) {
108        return -1;
109    }
110
111    if (sink(new_data, new_size, token) < new_size) {
112        printf("short write of output: %d (%s)\n", errno, strerror(errno));
113        return 1;
114    }
115    if (ctx) {
116        SHA_update(ctx, new_data, new_size);
117    }
118    free(new_data);
119
120    return 0;
121}
122
123int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size,
124                        const Value* patch, ssize_t patch_offset,
125                        unsigned char** new_data, ssize_t* new_size) {
126    // Patch data format:
127    //   0       8       "BSDIFF40"
128    //   8       8       X
129    //   16      8       Y
130    //   24      8       sizeof(newfile)
131    //   32      X       bzip2(control block)
132    //   32+X    Y       bzip2(diff block)
133    //   32+X+Y  ???     bzip2(extra block)
134    // with control block a set of triples (x,y,z) meaning "add x bytes
135    // from oldfile to x bytes from the diff block; copy y bytes from the
136    // extra block; seek forwards in oldfile by z bytes".
137
138    unsigned char* header = (unsigned char*) patch->data + patch_offset;
139    if (memcmp(header, "BSDIFF40", 8) != 0) {
140        printf("corrupt bsdiff patch file header (magic number)\n");
141        return 1;
142    }
143
144    ssize_t ctrl_len, data_len;
145    ctrl_len = offtin(header+8);
146    data_len = offtin(header+16);
147    *new_size = offtin(header+24);
148
149    if (ctrl_len < 0 || data_len < 0 || *new_size < 0) {
150        printf("corrupt patch file header (data lengths)\n");
151        return 1;
152    }
153
154    int bzerr;
155
156    bz_stream cstream;
157    cstream.next_in = patch->data + patch_offset + 32;
158    cstream.avail_in = ctrl_len;
159    cstream.bzalloc = NULL;
160    cstream.bzfree = NULL;
161    cstream.opaque = NULL;
162    if ((bzerr = BZ2_bzDecompressInit(&cstream, 0, 0)) != BZ_OK) {
163        printf("failed to bzinit control stream (%d)\n", bzerr);
164    }
165
166    bz_stream dstream;
167    dstream.next_in = patch->data + patch_offset + 32 + ctrl_len;
168    dstream.avail_in = data_len;
169    dstream.bzalloc = NULL;
170    dstream.bzfree = NULL;
171    dstream.opaque = NULL;
172    if ((bzerr = BZ2_bzDecompressInit(&dstream, 0, 0)) != BZ_OK) {
173        printf("failed to bzinit diff stream (%d)\n", bzerr);
174    }
175
176    bz_stream estream;
177    estream.next_in = patch->data + patch_offset + 32 + ctrl_len + data_len;
178    estream.avail_in = patch->size - (patch_offset + 32 + ctrl_len + data_len);
179    estream.bzalloc = NULL;
180    estream.bzfree = NULL;
181    estream.opaque = NULL;
182    if ((bzerr = BZ2_bzDecompressInit(&estream, 0, 0)) != BZ_OK) {
183        printf("failed to bzinit extra stream (%d)\n", bzerr);
184    }
185
186    *new_data = malloc(*new_size);
187    if (*new_data == NULL) {
188        printf("failed to allocate %ld bytes of memory for output file\n",
189               (long)*new_size);
190        return 1;
191    }
192
193    off_t oldpos = 0, newpos = 0;
194    off_t ctrl[3];
195    off_t len_read;
196    int i;
197    unsigned char buf[24];
198    while (newpos < *new_size) {
199        // Read control data
200        if (FillBuffer(buf, 24, &cstream) != 0) {
201            printf("error while reading control stream\n");
202            return 1;
203        }
204        ctrl[0] = offtin(buf);
205        ctrl[1] = offtin(buf+8);
206        ctrl[2] = offtin(buf+16);
207
208        // Sanity check
209        if (newpos + ctrl[0] > *new_size) {
210            printf("corrupt patch (new file overrun)\n");
211            return 1;
212        }
213
214        // Read diff string
215        if (FillBuffer(*new_data + newpos, ctrl[0], &dstream) != 0) {
216            printf("error while reading diff stream\n");
217            return 1;
218        }
219
220        // Add old data to diff string
221        for (i = 0; i < ctrl[0]; ++i) {
222            if ((oldpos+i >= 0) && (oldpos+i < old_size)) {
223                (*new_data)[newpos+i] += old_data[oldpos+i];
224            }
225        }
226
227        // Adjust pointers
228        newpos += ctrl[0];
229        oldpos += ctrl[0];
230
231        // Sanity check
232        if (newpos + ctrl[1] > *new_size) {
233            printf("corrupt patch (new file overrun)\n");
234            return 1;
235        }
236
237        // Read extra string
238        if (FillBuffer(*new_data + newpos, ctrl[1], &estream) != 0) {
239            printf("error while reading extra stream\n");
240            return 1;
241        }
242
243        // Adjust pointers
244        newpos += ctrl[1];
245        oldpos += ctrl[2];
246    }
247
248    BZ2_bzDecompressEnd(&cstream);
249    BZ2_bzDecompressEnd(&dstream);
250    BZ2_bzDecompressEnd(&estream);
251    return 0;
252}
253