1 /* Copyright (C) 2010-2020 The RetroArch team
2 *
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (archive_file_zlib.c).
5 * ---------------------------------------------------------------------------------------
6 *
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <file/archive_file.h>
27 #include <streams/file_stream.h>
28 #include <streams/trans_stream.h>
29 #include <retro_inline.h>
30 #include <retro_miscellaneous.h>
31 #include <encodings/crc32.h>
32
33 /* Only for MAX_WBITS */
34 #include <zlib.h>
35
36 #ifndef CENTRAL_FILE_HEADER_SIGNATURE
37 #define CENTRAL_FILE_HEADER_SIGNATURE 0x02014b50
38 #endif
39
40 #ifndef END_OF_CENTRAL_DIR_SIGNATURE
41 #define END_OF_CENTRAL_DIR_SIGNATURE 0x06054b50
42 #endif
43
44 enum file_archive_compression_mode
45 {
46 ZIP_MODE_STORED = 0,
47 ZIP_MODE_DEFLATED = 8
48 };
49
50 typedef struct
51 {
52 struct file_archive_transfer *state;
53 uint8_t *directory;
54 uint8_t *directory_entry;
55 uint8_t *directory_end;
56 void *current_stream;
57 uint8_t *compressed_data;
58 uint8_t *decompressed_data;
59 } zip_context_t;
60
read_le(const uint8_t * data,unsigned size)61 static INLINE uint32_t read_le(const uint8_t *data, unsigned size)
62 {
63 unsigned i;
64 uint32_t val = 0;
65
66 size *= 8;
67 for (i = 0; i < size; i += 8)
68 val |= (uint32_t)*data++ << i;
69
70 return val;
71 }
72
zip_context_free_stream(zip_context_t * zip_context,bool keep_decompressed)73 static void zip_context_free_stream(
74 zip_context_t *zip_context, bool keep_decompressed)
75 {
76 if (zip_context->current_stream)
77 {
78 zlib_inflate_backend.stream_free(zip_context->current_stream);
79 zip_context->current_stream = NULL;
80 }
81 if (zip_context->compressed_data)
82 {
83 #ifdef HAVE_MMAP
84 if (!zip_context->state->archive_mmap_data)
85 #endif
86 {
87 free(zip_context->compressed_data);
88 zip_context->compressed_data = NULL;
89 }
90 }
91 if (zip_context->decompressed_data && !keep_decompressed)
92 {
93 free(zip_context->decompressed_data);
94 zip_context->decompressed_data = NULL;
95 }
96 }
97
zlib_stream_decompress_data_to_file_init(void * context,file_archive_file_handle_t * handle,const uint8_t * cdata,unsigned cmode,uint32_t csize,uint32_t size)98 static bool zlib_stream_decompress_data_to_file_init(
99 void *context, file_archive_file_handle_t *handle,
100 const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size)
101 {
102 zip_context_t *zip_context = (zip_context_t *)context;
103 struct file_archive_transfer *state = zip_context->state;
104 uint8_t local_header_buf[4];
105 uint8_t *local_header;
106 uint32_t offsetNL, offsetEL;
107 int64_t offsetData;
108
109 /* free previous data and stream if left unfinished */
110 zip_context_free_stream(zip_context, false);
111
112 /* seek past most of the local directory header */
113 #ifdef HAVE_MMAP
114 if (state->archive_mmap_data)
115 {
116 local_header = state->archive_mmap_data + (size_t)cdata + 26;
117 }
118 else
119 #endif
120 {
121 filestream_seek(state->archive_file, (int64_t)(size_t)cdata + 26, RETRO_VFS_SEEK_POSITION_START);
122 if (filestream_read(state->archive_file, local_header_buf, 4) != 4)
123 goto error;
124 local_header = local_header_buf;
125 }
126
127 offsetNL = read_le(local_header, 2); /* file name length */
128 offsetEL = read_le(local_header + 2, 2); /* extra field length */
129 offsetData = (int64_t)(size_t)cdata + 26 + 4 + offsetNL + offsetEL;
130
131 #ifdef HAVE_MMAP
132 if (state->archive_mmap_data)
133 {
134 zip_context->compressed_data = state->archive_mmap_data + (size_t)offsetData;
135 }
136 else
137 #endif
138 {
139 /* allocate memory for the compressed data */
140 zip_context->compressed_data = (uint8_t*)malloc(csize);
141 if (!zip_context->compressed_data)
142 goto error;
143
144 /* skip over name and extra data */
145 filestream_seek(state->archive_file, offsetData, RETRO_VFS_SEEK_POSITION_START);
146 if (filestream_read(state->archive_file, zip_context->compressed_data, csize) != csize)
147 goto error;
148 }
149
150 switch (cmode)
151 {
152 case ZIP_MODE_STORED:
153 handle->data = zip_context->compressed_data;
154 return true;
155
156 case ZIP_MODE_DEFLATED:
157 zip_context->current_stream = zlib_inflate_backend.stream_new();
158 if (!zip_context->current_stream)
159 goto error;
160
161 if (zlib_inflate_backend.define)
162 zlib_inflate_backend.define(zip_context->current_stream, "window_bits", (uint32_t)-MAX_WBITS);
163
164 zip_context->decompressed_data = (uint8_t*)malloc(size);
165
166 if (!zip_context->decompressed_data)
167 goto error;
168
169 zlib_inflate_backend.set_in(zip_context->current_stream,
170 zip_context->compressed_data, csize);
171 zlib_inflate_backend.set_out(zip_context->current_stream,
172 zip_context->decompressed_data, size);
173
174 return true;
175 }
176
177 error:
178 zip_context_free_stream(zip_context, false);
179 return false;
180 }
181
zlib_stream_decompress_data_to_file_iterate(void * context,file_archive_file_handle_t * handle)182 static int zlib_stream_decompress_data_to_file_iterate(
183 void *context, file_archive_file_handle_t *handle)
184 {
185 zip_context_t *zip_context = (zip_context_t *)context;
186 bool zstatus;
187 uint32_t rd, wn;
188 enum trans_stream_error terror;
189
190 if (!zip_context->current_stream)
191 {
192 /* file was uncompressed or decompression finished before */
193 return 1;
194 }
195
196 zstatus = zlib_inflate_backend.trans(zip_context->current_stream, false, &rd, &wn, &terror);
197
198 if (zstatus && !terror)
199 {
200 /* successfully decompressed entire file */
201 zip_context_free_stream(zip_context, true);
202 handle->data = zip_context->decompressed_data;
203 return 1;
204 }
205
206 if (!zstatus && terror != TRANS_STREAM_ERROR_BUFFER_FULL)
207 {
208 /* error during stream processing */
209 zip_context_free_stream(zip_context, false);
210 return -1;
211 }
212
213 /* still more data to process */
214 return 0;
215 }
216
zlib_stream_crc32_calculate(uint32_t crc,const uint8_t * data,size_t length)217 static uint32_t zlib_stream_crc32_calculate(uint32_t crc,
218 const uint8_t *data, size_t length)
219 {
220 return encoding_crc32(crc, data, length);
221 }
222
zip_file_decompressed_handle(file_archive_transfer_t * transfer,file_archive_file_handle_t * handle,const uint8_t * cdata,unsigned cmode,uint32_t csize,uint32_t size,uint32_t crc32)223 static bool zip_file_decompressed_handle(
224 file_archive_transfer_t *transfer,
225 file_archive_file_handle_t* handle,
226 const uint8_t *cdata, unsigned cmode, uint32_t csize,
227 uint32_t size, uint32_t crc32)
228 {
229 int ret = 0;
230
231 transfer->backend = &zlib_backend;
232
233 if (!transfer->backend->stream_decompress_data_to_file_init(
234 transfer->context, handle, cdata, cmode, csize, size))
235 return false;
236
237 do
238 {
239 ret = transfer->backend->stream_decompress_data_to_file_iterate(
240 transfer->context, handle);
241 }while (ret == 0);
242
243 #if 0
244 handle->real_checksum = transfer->backend->stream_crc_calculate(0,
245 handle->data, size);
246
247 if (handle->real_checksum != crc32)
248 {
249 if (handle->data)
250 free(handle->data);
251
252 handle->data = NULL;
253 return false;
254 }
255 #endif
256
257 return true;
258 }
259
260 typedef struct
261 {
262 char *opt_file;
263 char *needle;
264 void **buf;
265 size_t size;
266 bool found;
267 } decomp_state_t;
268
269 /* Extract the relative path (needle) from a
270 * ZIP archive (path) and allocate a buffer for it to write it in.
271 *
272 * optional_outfile if not NULL will be used to extract the file to.
273 * buf will be 0 then.
274 */
275
zip_file_decompressed(const char * name,const char * valid_exts,const uint8_t * cdata,unsigned cmode,uint32_t csize,uint32_t size,uint32_t crc32,struct archive_extract_userdata * userdata)276 static int zip_file_decompressed(
277 const char *name, const char *valid_exts,
278 const uint8_t *cdata, unsigned cmode,
279 uint32_t csize, uint32_t size,
280 uint32_t crc32, struct archive_extract_userdata *userdata)
281 {
282 decomp_state_t* decomp_state = (decomp_state_t*)userdata->cb_data;
283 char last_char = name[strlen(name) - 1];
284 /* Ignore directories. */
285 if (last_char == '/' || last_char == '\\')
286 return 1;
287
288 if (strstr(name, decomp_state->needle))
289 {
290 file_archive_file_handle_t handle = {0};
291
292 if (zip_file_decompressed_handle(userdata->transfer,
293 &handle, cdata, cmode, csize, size, crc32))
294 {
295 if (decomp_state->opt_file != 0)
296 {
297 /* Called in case core has need_fullpath enabled. */
298 bool success = filestream_write_file(decomp_state->opt_file, handle.data, size);
299
300 free(handle.data);
301 handle.data = NULL;
302
303 decomp_state->size = 0;
304
305 if (!success)
306 return -1;
307 }
308 else
309 {
310 /* Called in case core has need_fullpath disabled.
311 * Will move decompressed content directly into
312 * RetroArch's ROM buffer. */
313 *decomp_state->buf = handle.data;
314 handle.data = NULL;
315
316 decomp_state->size = size;
317 }
318 }
319
320 decomp_state->found = true;
321 }
322
323 return 1;
324 }
325
zip_file_read(const char * path,const char * needle,void ** buf,const char * optional_outfile)326 static int64_t zip_file_read(
327 const char *path,
328 const char *needle, void **buf,
329 const char *optional_outfile)
330 {
331 file_archive_transfer_t state = {ARCHIVE_TRANSFER_INIT};
332 decomp_state_t decomp = {0};
333 struct archive_extract_userdata userdata = {0};
334 bool returnerr = true;
335 int ret = 0;
336
337 if (needle)
338 decomp.needle = strdup(needle);
339 if (optional_outfile)
340 decomp.opt_file = strdup(optional_outfile);
341
342 userdata.transfer = &state;
343 userdata.cb_data = &decomp;
344 decomp.buf = buf;
345
346 do
347 {
348 ret = file_archive_parse_file_iterate(&state, &returnerr, path,
349 "", zip_file_decompressed, &userdata);
350 if (!returnerr)
351 break;
352 }while (ret == 0 && !decomp.found);
353
354 file_archive_parse_file_iterate_stop(&state);
355
356 if (decomp.opt_file)
357 free(decomp.opt_file);
358 if (decomp.needle)
359 free(decomp.needle);
360
361 if (!decomp.found)
362 return -1;
363
364 return (int64_t)decomp.size;
365 }
366
zip_parse_file_init(file_archive_transfer_t * state,const char * file)367 static int zip_parse_file_init(file_archive_transfer_t *state,
368 const char *file)
369 {
370 uint8_t footer_buf[1024];
371 uint8_t *footer = footer_buf;
372 int64_t read_pos = state->archive_size;
373 int64_t read_block = MIN(read_pos, sizeof(footer_buf));
374 int64_t directory_size, directory_offset;
375 zip_context_t *zip_context = NULL;
376
377 /* Minimal ZIP file size is 22 bytes */
378 if (read_block < 22)
379 return -1;
380
381 /* Find the end of central directory record by scanning
382 * the file from the end towards the beginning.
383 */
384 for (;;)
385 {
386 if (--footer < footer_buf)
387 {
388 if (read_pos <= 0)
389 return -1; /* reached beginning of file */
390
391 /* Read 21 bytes of overlaps except on the first block. */
392 if (read_pos == state->archive_size)
393 read_pos = read_pos - read_block;
394 else
395 read_pos = MAX(read_pos - read_block + 21, 0);
396
397 /* Seek to read_pos and read read_block bytes. */
398 filestream_seek(state->archive_file, read_pos, RETRO_VFS_SEEK_POSITION_START);
399 if (filestream_read(state->archive_file, footer_buf, read_block) != read_block)
400 return -1;
401
402 footer = footer_buf + read_block - 22;
403 }
404 if (read_le(footer, 4) == END_OF_CENTRAL_DIR_SIGNATURE)
405 {
406 unsigned comment_len = read_le(footer + 20, 2);
407 if (read_pos + (footer - footer_buf) + 22 + comment_len == state->archive_size)
408 break; /* found it! */
409 }
410 }
411
412 /* Read directory info and do basic sanity checks. */
413 directory_size = read_le(footer + 12, 4);
414 directory_offset = read_le(footer + 16, 4);
415 if (directory_size > state->archive_size
416 || directory_offset > state->archive_size)
417 return -1;
418
419 /* This is a ZIP file, allocate one block of memory for both the
420 * context and the entire directory, then read the directory.
421 */
422 zip_context = (zip_context_t*)malloc(sizeof(zip_context_t) + (size_t)directory_size);
423 zip_context->state = state;
424 zip_context->directory = (uint8_t*)(zip_context + 1);
425 zip_context->directory_entry = zip_context->directory;
426 zip_context->directory_end = zip_context->directory + (size_t)directory_size;
427 zip_context->current_stream = NULL;
428 zip_context->compressed_data = NULL;
429 zip_context->decompressed_data = NULL;
430
431 filestream_seek(state->archive_file, directory_offset, RETRO_VFS_SEEK_POSITION_START);
432 if (filestream_read(state->archive_file, zip_context->directory, directory_size) != directory_size)
433 {
434 free(zip_context);
435 return -1;
436 }
437
438 state->context = zip_context;
439 state->step_total = read_le(footer + 10, 2); /* total entries */;
440
441 return 0;
442 }
443
zip_parse_file_iterate_step_internal(zip_context_t * zip_context,char * filename,const uint8_t ** cdata,unsigned * cmode,uint32_t * size,uint32_t * csize,uint32_t * checksum,unsigned * payback)444 static int zip_parse_file_iterate_step_internal(
445 zip_context_t * zip_context, char *filename,
446 const uint8_t **cdata,
447 unsigned *cmode, uint32_t *size, uint32_t *csize,
448 uint32_t *checksum, unsigned *payback)
449 {
450 uint8_t *entry = zip_context->directory_entry;
451 uint32_t signature, namelength, extralength, commentlength, offset;
452
453 if (entry < zip_context->directory || entry >= zip_context->directory_end)
454 return 0;
455
456 signature = read_le(zip_context->directory_entry + 0, 4);
457
458 if (signature != CENTRAL_FILE_HEADER_SIGNATURE)
459 return 0;
460
461 *cmode = read_le(zip_context->directory_entry + 10, 2); /* compression mode, 0 = store, 8 = deflate */
462 *checksum = read_le(zip_context->directory_entry + 16, 4); /* CRC32 */
463 *csize = read_le(zip_context->directory_entry + 20, 4); /* compressed size */
464 *size = read_le(zip_context->directory_entry + 24, 4); /* uncompressed size */
465
466 namelength = read_le(zip_context->directory_entry + 28, 2); /* file name length */
467 extralength = read_le(zip_context->directory_entry + 30, 2); /* extra field length */
468 commentlength = read_le(zip_context->directory_entry + 32, 2); /* file comment length */
469
470 if (namelength >= PATH_MAX_LENGTH)
471 return -1;
472
473 memcpy(filename, zip_context->directory_entry + 46, namelength); /* file name */
474 filename[namelength] = '\0';
475
476 offset = read_le(zip_context->directory_entry + 42, 4); /* relative offset of local file header */
477
478 *cdata = (uint8_t*)(size_t)offset; /* store file offset in data pointer */
479
480 *payback = 46 + namelength + extralength + commentlength;
481
482 return 1;
483 }
484
zip_parse_file_iterate_step(void * context,const char * valid_exts,struct archive_extract_userdata * userdata,file_archive_file_cb file_cb)485 static int zip_parse_file_iterate_step(void *context,
486 const char *valid_exts, struct archive_extract_userdata *userdata,
487 file_archive_file_cb file_cb)
488 {
489 zip_context_t *zip_context = (zip_context_t *)context;
490 const uint8_t *cdata = NULL;
491 uint32_t checksum = 0;
492 uint32_t size = 0;
493 uint32_t csize = 0;
494 unsigned cmode = 0;
495 unsigned payload = 0;
496 int ret = zip_parse_file_iterate_step_internal(zip_context,
497 userdata->current_file_path, &cdata, &cmode, &size, &csize, &checksum, &payload);
498
499 if (ret != 1)
500 return ret;
501
502 userdata->crc = checksum;
503
504 if (file_cb && !file_cb(userdata->current_file_path, valid_exts, cdata, cmode,
505 csize, size, checksum, userdata))
506 return 0;
507
508 zip_context->directory_entry += payload;
509
510 return 1;
511 }
512
zip_parse_file_free(void * context)513 static void zip_parse_file_free(void *context)
514 {
515 zip_context_t *zip_context = (zip_context_t *)context;
516 zip_context_free_stream(zip_context, false);
517 free(zip_context);
518 }
519
520 const struct file_archive_file_backend zlib_backend = {
521 zip_parse_file_init,
522 zip_parse_file_iterate_step,
523 zip_parse_file_free,
524 zlib_stream_decompress_data_to_file_init,
525 zlib_stream_decompress_data_to_file_iterate,
526 zlib_stream_crc32_calculate,
527 zip_file_read,
528 "zlib"
529 };
530