OSDN Git Service

Merge change 2594 into donut
[android-x86/build.git] / tools / applypatch / imgpatch.c
1 /*
2  * Copyright (C) 2009 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 // See imgdiff.c in this directory for a description of the patch file
18 // format.
19
20 #include <stdio.h>
21 #include <sys/stat.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <string.h>
25
26 #include "zlib.h"
27 #include "mincrypt/sha.h"
28 #include "applypatch.h"
29 #include "imgdiff.h"
30
31 int Read4(unsigned char* p) {
32   return (int)(((unsigned int)p[3] << 24) |
33                ((unsigned int)p[2] << 16) |
34                ((unsigned int)p[1] << 8) |
35                (unsigned int)p[0]);
36 }
37
38 long long Read8(unsigned char* p) {
39   return (long long)(((unsigned long long)p[7] << 56) |
40                      ((unsigned long long)p[6] << 48) |
41                      ((unsigned long long)p[5] << 40) |
42                      ((unsigned long long)p[4] << 32) |
43                      ((unsigned long long)p[3] << 24) |
44                      ((unsigned long long)p[2] << 16) |
45                      ((unsigned long long)p[1] << 8) |
46                      (unsigned long long)p[0]);
47 }
48
49 /*
50  * Apply the patch given in 'patch_filename' to the source data given
51  * by (old_data, old_size).  Write the patched output to the 'output'
52  * file, and update the SHA context with the output data as well.
53  * Return 0 on success.
54  */
55 int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size,
56                     const char* patch_filename,
57                     FILE* output, SHA_CTX* ctx) {
58   FILE* f;
59   if ((f = fopen(patch_filename, "rb")) == NULL) {
60     fprintf(stderr, "failed to open patch file\n");
61     return -1;
62   }
63
64   unsigned char header[12];
65   if (fread(header, 1, 12, f) != 12) {
66     fprintf(stderr, "failed to read patch file header\n");
67     return -1;
68   }
69
70   if (memcmp(header, "IMGDIFF1", 8) != 0) {
71     fprintf(stderr, "corrupt patch file header (magic number)\n");
72     return -1;
73   }
74
75   int num_chunks = Read4(header+8);
76
77   int i;
78   for (i = 0; i < num_chunks; ++i) {
79     // each chunk's header record starts with 28 bytes (4 + 8*3).
80     unsigned char chunk[28];
81     if (fread(chunk, 1, 28, f) != 28) {
82       fprintf(stderr, "failed to read chunk %d record\n", i);
83       return -1;
84     }
85
86     int type = Read4(chunk);
87     size_t src_start = Read8(chunk+4);
88     size_t src_len = Read8(chunk+12);
89     size_t patch_offset = Read8(chunk+20);
90
91     if (type == CHUNK_NORMAL) {
92       fprintf(stderr, "CHUNK %d:  normal   patch offset %d\n", i, patch_offset);
93
94       ApplyBSDiffPatch(old_data + src_start, src_len,
95                        patch_filename, patch_offset,
96                        output, ctx);
97     } else if (type == CHUNK_GZIP) {
98       fprintf(stderr, "CHUNK %d:  gzip     patch offset %d\n", i, patch_offset);
99
100       // gzip chunks have an additional 40 + gzip_header_len + 8 bytes
101       // in their chunk header.
102       unsigned char* gzip = malloc(40);
103       if (fread(gzip, 1, 40, f) != 40) {
104         fprintf(stderr, "failed to read chunk %d initial gzip data\n", i);
105         return -1;
106       }
107       size_t gzip_header_len = Read4(gzip+36);
108       gzip = realloc(gzip, 40 + gzip_header_len + 8);
109       if (fread(gzip+40, 1, gzip_header_len+8, f) != gzip_header_len+8) {
110         fprintf(stderr, "failed to read chunk %d remaining gzip data\n", i);
111         return -1;
112       }
113
114       size_t expanded_len = Read8(gzip);
115       size_t target_len = Read8(gzip);
116       int gz_level = Read4(gzip+16);
117       int gz_method = Read4(gzip+20);
118       int gz_windowBits = Read4(gzip+24);
119       int gz_memLevel = Read4(gzip+28);
120       int gz_strategy = Read4(gzip+32);
121
122       // Decompress the source data; the chunk header tells us exactly
123       // how big we expect it to be when decompressed.
124
125       unsigned char* expanded_source = malloc(expanded_len);
126       if (expanded_source == NULL) {
127         fprintf(stderr, "failed to allocate %d bytes for expanded_source\n", expanded_len);
128         return -1;
129       }
130
131       z_stream strm;
132       strm.zalloc = Z_NULL;
133       strm.zfree = Z_NULL;
134       strm.opaque = Z_NULL;
135       strm.avail_in = src_len - (gzip_header_len + 8);
136       strm.next_in = (unsigned char*)(old_data + src_start + gzip_header_len);
137       strm.avail_out = expanded_len;
138       strm.next_out = expanded_source;
139
140       int ret;
141       ret = inflateInit2(&strm, -15);
142       if (ret != Z_OK) {
143         fprintf(stderr, "failed to init source inflation: %d\n", ret);
144         return -1;
145       }
146
147       // Because we've provided enough room to accommodate the output
148       // data, we expect one call to inflate() to suffice.
149       ret = inflate(&strm, Z_SYNC_FLUSH);
150       if (ret != Z_STREAM_END) {
151         fprintf(stderr, "source inflation returned %d\n", ret);
152         return -1;
153       }
154       // We should have filled the output buffer exactly.
155       if (strm.avail_out != 0) {
156         fprintf(stderr, "source inflation short by %d bytes\n", strm.avail_out);
157         return -1;
158       }
159       inflateEnd(&strm);
160
161       // Next, apply the bsdiff patch (in memory) to the uncompressed
162       // data.
163       unsigned char* uncompressed_target_data;
164       ssize_t uncompressed_target_size;
165       if (ApplyBSDiffPatchMem(expanded_source, expanded_len,
166                               patch_filename, patch_offset,
167                               &uncompressed_target_data,
168                               &uncompressed_target_size) != 0) {
169         return -1;
170       }
171
172       // Now compress the target data and append it to the output.
173
174       // start with the gzip header.
175       fwrite(gzip+40, 1, gzip_header_len, output);
176       SHA_update(ctx, gzip+40, gzip_header_len);
177
178       // we're done with the expanded_source data buffer, so we'll
179       // reuse that memory to receive the output of deflate.
180       unsigned char* temp_data = expanded_source;
181       ssize_t temp_size = expanded_len;
182       if (temp_size < 32768) {
183         // ... unless the buffer is too small, in which case we'll
184         // allocate a fresh one.
185         free(temp_data);
186         temp_data = malloc(32768);
187         temp_size = 32768;
188       }
189
190       // now the deflate stream
191       strm.zalloc = Z_NULL;
192       strm.zfree = Z_NULL;
193       strm.opaque = Z_NULL;
194       strm.avail_in = uncompressed_target_size;
195       strm.next_in = uncompressed_target_data;
196       ret = deflateInit2(&strm, gz_level, gz_method, gz_windowBits,
197                          gz_memLevel, gz_strategy);
198       do {
199         strm.avail_out = temp_size;
200         strm.next_out = temp_data;
201         ret = deflate(&strm, Z_FINISH);
202         size_t have = temp_size - strm.avail_out;
203
204         if (fwrite(temp_data, 1, have, output) != have) {
205           fprintf(stderr, "failed to write %d compressed bytes to output\n",
206                   have);
207           return -1;
208         }
209         SHA_update(ctx, temp_data, have);
210       } while (ret != Z_STREAM_END);
211       deflateEnd(&strm);
212
213       // lastly, the gzip footer.
214       fwrite(gzip+40+gzip_header_len, 1, 8, output);
215       SHA_update(ctx, gzip+40+gzip_header_len, 8);
216
217       free(temp_data);
218       free(uncompressed_target_data);
219       free(gzip);
220     } else {
221       fprintf(stderr, "patch chunk %d is unknown type %d\n", i, type);
222       return -1;
223     }
224   }
225
226   return 0;
227 }