diff options
author | neonloop | 2021-08-04 15:09:12 +0000 |
---|---|---|
committer | neonloop | 2021-08-04 15:09:12 +0000 |
commit | 99632f66e74fc57c463072be312d634aeb67bc61 (patch) | |
tree | e4ccaf52b93d04c69865d82556e2ce4cd3a6c599 /unzip.c | |
download | picoarch-99632f66e74fc57c463072be312d634aeb67bc61.tar.gz picoarch-99632f66e74fc57c463072be312d634aeb67bc61.tar.bz2 picoarch-99632f66e74fc57c463072be312d634aeb67bc61.zip |
Initial commit
Diffstat (limited to 'unzip.c')
-rw-r--r-- | unzip.c | 200 |
1 files changed, 200 insertions, 0 deletions
@@ -0,0 +1,200 @@ +#include <stdlib.h> +#include <string.h> +#include "main.h" +#include "unzip.h" +#include "zlib.h" + +#define HEADER_SIZE 30 +#define CHUNK 65536 + +#define LE_READ16(buf) ((uint16_t)(((uint8_t *)(buf))[1] << 8 | ((uint8_t *)(buf))[0])) +#define LE_READ32(buf) ((uint32_t)(((uint8_t *)(buf))[3] << 24 | ((uint8_t *)(buf))[2] << 16 | ((uint8_t *)(buf))[1] << 8 | ((uint8_t *)(buf))[0])) + +struct file_info { + uint32_t compressed_size; + int (*inflate)(FILE *zip, FILE *dest, size_t size); + char filename[MAX_PATH]; +}; + +static int write_uncompressed(FILE *zip, FILE *dest, size_t size) { + uint8_t buf[CHUNK]; + + while (size) { + size_t wsize = MIN(size, CHUNK); + + if (size != fread(buf, 1, wsize, zip)) { + return -1; + } + if (size != fwrite(buf, 1, wsize, zip)) { + return -1; + } + size -= wsize; + } + return 0; +} + +static int write_inflate(FILE *zip, FILE *dest, size_t size) { + z_stream stream = {0}; + size_t have = 0; + uint8_t in[CHUNK]; + uint8_t out[CHUNK]; + int ret = -1; + + ret = inflateInit2(&stream, -MAX_WBITS); + if (ret != Z_OK) + return ret; + + do { + size_t insize = MIN(size, CHUNK); + + stream.avail_in = fread(in, 1, insize, zip); + if (ferror(zip)) { + (void)inflateEnd(&stream); + return Z_ERRNO; + } + + if (!stream.avail_in) + break; + stream.next_in = in; + + do { + stream.avail_out = CHUNK; + stream.next_out = out; + + ret = inflate(&stream, Z_NO_FLUSH); + switch(ret) { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; + case Z_DATA_ERROR: + case Z_MEM_ERROR: + (void)inflateEnd(&stream); + return ret; + } + + have = CHUNK - stream.avail_out; + if (fwrite(out, 1, have, dest) != have || ferror(dest)) { + (void)inflateEnd(&stream); + return Z_ERRNO; + } + } while (stream.avail_out == 0); + + size -= insize; + } while (size && ret != Z_STREAM_END); + + (void)inflateEnd(&stream); + + if (!size || ret == Z_STREAM_END) { + return Z_OK; + } else { + return Z_DATA_ERROR; + } +} + +static int find_entry(FILE *zip, const char **extensions, struct file_info *info) { + int ret = -1; + uint8_t header[HEADER_SIZE]; + uint32_t next = 0; + uint16_t file_name_size = 0; + char extension[10]; + + while(1) { + bool match = false; + + if (next) + fseek(zip, next, SEEK_CUR); + + if (HEADER_SIZE != fread(header, 1, HEADER_SIZE, zip)) + break; + + if (header[0] != 0x50 || header[1] != 0x4b || header[2] != 0x03 || header[3] != 0x04) + break; + + if ((uint16_t)(header[6]) & 0x0008) { + PA_ERROR("Unsupported zip file\n"); + break; + } + + file_name_size = LE_READ16(&header[26]); + if (file_name_size >= MAX_PATH) + break; + + if (file_name_size != fread(info->filename, 1, file_name_size, zip)) + break; + + info->filename[file_name_size] = '\0'; + info->compressed_size = LE_READ32(&header[18]); + + fseek(zip, LE_READ16(&header[28]), SEEK_CUR); + + next = info->compressed_size; + + for (int i = 0; extensions[i]; i++) { + snprintf(extension, 10, ".%s", extensions[i]); + if (has_suffix_i(info->filename, extension)) { + match = true; + break; + } + } + + if (!match) + continue; + + switch (LE_READ16(&header[8])) { + case 0: /* Uncompressed */ + info->inflate = write_uncompressed; + case 8: /* DEFLATE */ + info->inflate = write_inflate; + } + + ret = 0; + break; + } + return ret; +} + +int unzip_tmp(FILE *zip, const char **extensions, char *filename, size_t len) { + int ret = -1; + struct file_info info = {0}; + FILE *dest = NULL; + + if (!find_entry(zip, extensions, &info)) { + int fd = 0; + snprintf(filename, len, "/tmp/pa-XXXXXX%s", basename(info.filename)); + + fd = mkstemps(filename, strlen(info.filename)); + dest = fdopen(fd, "w"); + + if (!dest) { + PA_ERROR("Error creating temporary file for decompression\n"); + goto finish; + } + + if (info.inflate(zip, dest, info.compressed_size)) { + PA_ERROR("Error decompressing file\n"); + goto finish; + } + + ret = 0; + } + +finish: + if (dest) + fclose(dest); + return ret; +} + +int unzip(FILE *zip, const char **extensions, FILE *dest) { + int ret = -1; + struct file_info info = {0}; + + if(!find_entry(zip, extensions, &info)) { + if (!info.inflate(zip, dest, info.compressed_size)) { + ret = 0; + } else { + PA_ERROR("Error decompressing file\n"); + } + + } + + return ret; +} |