OSDN Git Service

add support for reading MTD partitions to applypatch
[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",
128                 expanded_len);
129         return -1;
130       }
131
132       z_stream strm;
133       strm.zalloc = Z_NULL;
134       strm.zfree = Z_NULL;
135       strm.opaque = Z_NULL;
136       strm.avail_in = src_len - (gzip_header_len + 8);
137       strm.next_in = (unsigned char*)(old_data + src_start + gzip_header_len);
138       strm.avail_out = expanded_len;
139       strm.next_out = expanded_source;
140
141       int ret;
142       ret = inflateInit2(&strm, -15);
143       if (ret != Z_OK) {
144         fprintf(stderr, "failed to init source inflation: %d\n", ret);
145         return -1;
146       }
147
148       // Because we've provided enough room to accommodate the output
149       // data, we expect one call to inflate() to suffice.
150       ret = inflate(&strm, Z_SYNC_FLUSH);
151       if (ret != Z_STREAM_END) {
152         fprintf(stderr, "source inflation returned %d\n", ret);
153         return -1;
154       }
155       // We should have filled the output buffer exactly.
156       if (strm.avail_out != 0) {
157         fprintf(stderr, "source inflation short by %d bytes\n", strm.avail_out);
158         return -1;
159       }
160       inflateEnd(&strm);
161
162       // Next, apply the bsdiff patch (in memory) to the uncompressed
163       // data.
164       unsigned char* uncompressed_target_data;
165       ssize_t uncompressed_target_size;
166       if (ApplyBSDiffPatchMem(expanded_source, expanded_len,
167                               patch_filename, patch_offset,
168                               &uncompressed_target_data,
169                               &uncompressed_target_size) != 0) {
170         return -1;
171       }
172
173       // Now compress the target data and append it to the output.
174
175       // start with the gzip header.
176       fwrite(gzip+40, 1, gzip_header_len, output);
177       SHA_update(ctx, gzip+40, gzip_header_len);
178
179       // we're done with the expanded_source data buffer, so we'll
180       // reuse that memory to receive the output of deflate.
181       unsigned char* temp_data = expanded_source;
182       ssize_t temp_size = expanded_len;
183       if (temp_size < 32768) {
184         // ... unless the buffer is too small, in which case we'll
185         // allocate a fresh one.
186         free(temp_data);
187         temp_data = malloc(32768);
188         temp_size = 32768;
189       }
190
191       // now the deflate stream
192       strm.zalloc = Z_NULL;
193       strm.zfree = Z_NULL;
194       strm.opaque = Z_NULL;
195       strm.avail_in = uncompressed_target_size;
196       strm.next_in = uncompressed_target_data;
197       ret = deflateInit2(&strm, gz_level, gz_method, gz_windowBits,
198                          gz_memLevel, gz_strategy);
199       do {
200         strm.avail_out = temp_size;
201         strm.next_out = temp_data;
202         ret = deflate(&strm, Z_FINISH);
203         size_t have = temp_size - strm.avail_out;
204
205         if (fwrite(temp_data, 1, have, output) != have) {
206           fprintf(stderr, "failed to write %d compressed bytes to output\n",
207                   have);
208           return -1;
209         }
210         SHA_update(ctx, temp_data, have);
211       } while (ret != Z_STREAM_END);
212       deflateEnd(&strm);
213
214       // lastly, the gzip footer.
215       fwrite(gzip+40+gzip_header_len, 1, 8, output);
216       SHA_update(ctx, gzip+40+gzip_header_len, 8);
217
218       free(temp_data);
219       free(uncompressed_target_data);
220       free(gzip);
221     } else {
222       fprintf(stderr, "patch chunk %d is unknown type %d\n", i, type);
223       return -1;
224     }
225   }
226
227   return 0;
228 }