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/cdefs.h> 22 #include <sys/stat.h> 23 #include <errno.h> 24 #include <unistd.h> 25 #include <string.h> 26 27 #include <vector> 28 29 #include "zlib.h" 30 #include "openssl/sha.h" 31 #include "applypatch.h" 32 #include "imgdiff.h" 33 #include "utils.h" 34 35 int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, 36 const unsigned char* patch_data, ssize_t patch_size, 37 SinkFn sink, void* token) { 38 Value patch = {VAL_BLOB, patch_size, 39 reinterpret_cast<char*>(const_cast<unsigned char*>(patch_data))}; 40 return ApplyImagePatch( 41 old_data, old_size, &patch, sink, token, nullptr, nullptr); 42 } 43 44 /* 45 * Apply the patch given in 'patch_filename' to the source data given 46 * by (old_data, old_size). Write the patched output to the 'output' 47 * file, and update the SHA context with the output data as well. 48 * Return 0 on success. 49 */ 50 int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, 51 const Value* patch, 52 SinkFn sink, void* token, SHA_CTX* ctx, 53 const Value* bonus_data) { 54 ssize_t pos = 12; 55 char* header = patch->data; 56 if (patch->size < 12) { 57 printf("patch too short to contain header\n"); 58 return -1; 59 } 60 61 // IMGDIFF2 uses CHUNK_NORMAL, CHUNK_DEFLATE, and CHUNK_RAW. 62 // (IMGDIFF1, which is no longer supported, used CHUNK_NORMAL and 63 // CHUNK_GZIP.) 64 if (memcmp(header, "IMGDIFF2", 8) != 0) { 65 printf("corrupt patch file header (magic number)\n"); 66 return -1; 67 } 68 69 int num_chunks = Read4(header+8); 70 71 int i; 72 for (i = 0; i < num_chunks; ++i) { 73 // each chunk's header record starts with 4 bytes. 74 if (pos + 4 > patch->size) { 75 printf("failed to read chunk %d record\n", i); 76 return -1; 77 } 78 int type = Read4(patch->data + pos); 79 pos += 4; 80 81 if (type == CHUNK_NORMAL) { 82 char* normal_header = patch->data + pos; 83 pos += 24; 84 if (pos > patch->size) { 85 printf("failed to read chunk %d normal header data\n", i); 86 return -1; 87 } 88 89 size_t src_start = Read8(normal_header); 90 size_t src_len = Read8(normal_header+8); 91 size_t patch_offset = Read8(normal_header+16); 92 93 if (src_start + src_len > static_cast<size_t>(old_size)) { 94 printf("source data too short\n"); 95 return -1; 96 } 97 ApplyBSDiffPatch(old_data + src_start, src_len, 98 patch, patch_offset, sink, token, ctx); 99 } else if (type == CHUNK_RAW) { 100 char* raw_header = patch->data + pos; 101 pos += 4; 102 if (pos > patch->size) { 103 printf("failed to read chunk %d raw header data\n", i); 104 return -1; 105 } 106 107 ssize_t data_len = Read4(raw_header); 108 109 if (pos + data_len > patch->size) { 110 printf("failed to read chunk %d raw data\n", i); 111 return -1; 112 } 113 if (ctx) SHA1_Update(ctx, patch->data + pos, data_len); 114 if (sink((unsigned char*)patch->data + pos, 115 data_len, token) != data_len) { 116 printf("failed to write chunk %d raw data\n", i); 117 return -1; 118 } 119 pos += data_len; 120 } else if (type == CHUNK_DEFLATE) { 121 // deflate chunks have an additional 60 bytes in their chunk header. 122 char* deflate_header = patch->data + pos; 123 pos += 60; 124 if (pos > patch->size) { 125 printf("failed to read chunk %d deflate header data\n", i); 126 return -1; 127 } 128 129 size_t src_start = Read8(deflate_header); 130 size_t src_len = Read8(deflate_header+8); 131 size_t patch_offset = Read8(deflate_header+16); 132 size_t expanded_len = Read8(deflate_header+24); 133 int level = Read4(deflate_header+40); 134 int method = Read4(deflate_header+44); 135 int windowBits = Read4(deflate_header+48); 136 int memLevel = Read4(deflate_header+52); 137 int strategy = Read4(deflate_header+56); 138 139 if (src_start + src_len > static_cast<size_t>(old_size)) { 140 printf("source data too short\n"); 141 return -1; 142 } 143 144 // Decompress the source data; the chunk header tells us exactly 145 // how big we expect it to be when decompressed. 146 147 // Note: expanded_len will include the bonus data size if 148 // the patch was constructed with bonus data. The 149 // deflation will come up 'bonus_size' bytes short; these 150 // must be appended from the bonus_data value. 151 size_t bonus_size = (i == 1 && bonus_data != NULL) ? bonus_data->size : 0; 152 153 std::vector<unsigned char> expanded_source(expanded_len); 154 155 // inflate() doesn't like strm.next_out being a nullptr even with 156 // avail_out being zero (Z_STREAM_ERROR). 157 if (expanded_len != 0) { 158 z_stream strm; 159 strm.zalloc = Z_NULL; 160 strm.zfree = Z_NULL; 161 strm.opaque = Z_NULL; 162 strm.avail_in = src_len; 163 strm.next_in = (unsigned char*)(old_data + src_start); 164 strm.avail_out = expanded_len; 165 strm.next_out = expanded_source.data(); 166 167 int ret; 168 ret = inflateInit2(&strm, -15); 169 if (ret != Z_OK) { 170 printf("failed to init source inflation: %d\n", ret); 171 return -1; 172 } 173 174 // Because we've provided enough room to accommodate the output 175 // data, we expect one call to inflate() to suffice. 176 ret = inflate(&strm, Z_SYNC_FLUSH); 177 if (ret != Z_STREAM_END) { 178 printf("source inflation returned %d\n", ret); 179 return -1; 180 } 181 // We should have filled the output buffer exactly, except 182 // for the bonus_size. 183 if (strm.avail_out != bonus_size) { 184 printf("source inflation short by %zu bytes\n", strm.avail_out-bonus_size); 185 return -1; 186 } 187 inflateEnd(&strm); 188 189 if (bonus_size) { 190 memcpy(expanded_source.data() + (expanded_len - bonus_size), 191 bonus_data->data, bonus_size); 192 } 193 } 194 195 // Next, apply the bsdiff patch (in memory) to the uncompressed 196 // data. 197 std::vector<unsigned char> uncompressed_target_data; 198 if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len, 199 patch, patch_offset, 200 &uncompressed_target_data) != 0) { 201 return -1; 202 } 203 204 // Now compress the target data and append it to the output. 205 206 // we're done with the expanded_source data buffer, so we'll 207 // reuse that memory to receive the output of deflate. 208 if (expanded_source.size() < 32768U) { 209 expanded_source.resize(32768U); 210 } 211 212 { 213 std::vector<unsigned char>& temp_data = expanded_source; 214 215 // now the deflate stream 216 z_stream strm; 217 strm.zalloc = Z_NULL; 218 strm.zfree = Z_NULL; 219 strm.opaque = Z_NULL; 220 strm.avail_in = uncompressed_target_data.size(); 221 strm.next_in = uncompressed_target_data.data(); 222 int ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy); 223 if (ret != Z_OK) { 224 printf("failed to init uncompressed data deflation: %d\n", ret); 225 return -1; 226 } 227 do { 228 strm.avail_out = temp_data.size(); 229 strm.next_out = temp_data.data(); 230 ret = deflate(&strm, Z_FINISH); 231 ssize_t have = temp_data.size() - strm.avail_out; 232 233 if (sink(temp_data.data(), have, token) != have) { 234 printf("failed to write %ld compressed bytes to output\n", 235 (long)have); 236 return -1; 237 } 238 if (ctx) SHA1_Update(ctx, temp_data.data(), have); 239 } while (ret != Z_STREAM_END); 240 deflateEnd(&strm); 241 } 242 } else { 243 printf("patch chunk %d is unknown type %d\n", i, type); 244 return -1; 245 } 246 } 247 248 return 0; 249 } 250