From 1b5e82440549ab161e26863baa1bb06cc9f2aa64 Mon Sep 17 00:00:00 2001 From: Amaury Pouly Date: Tue, 21 Jul 2020 15:15:10 +0200 Subject: [PATCH] nwztool/upgtool: add support for compression To decompress some output file(s), simply pass -z where idx is the index of the file to decompress, starting from 0. For example upgtool -e NW_WM_FW.UPG -o tmp/ -m nw-wm1a -z 6 -z 7 to decompress files 6 and 7. To compress file, use the same options: upgtool -c NW_WM_FW.UPG -m nw-wm1a -z 2 script.sh md5sum.txt system.img Change-Id: I1ef0b3e02c98e58154f1a959fb1ad70ad2ce6500 --- utils/nwztools/upgtools/Makefile | 2 +- utils/nwztools/upgtools/make_release.sh | 15 ++++ utils/nwztools/upgtools/upg.c | 41 ++++----- utils/nwztools/upgtools/upgtool.c | 107 +++++++++++++++++++++--- 4 files changed, 134 insertions(+), 31 deletions(-) create mode 100755 utils/nwztools/upgtools/make_release.sh diff --git a/utils/nwztools/upgtools/Makefile b/utils/nwztools/upgtools/Makefile index 287fe99c65..42b7a62e4c 100644 --- a/utils/nwztools/upgtools/Makefile +++ b/utils/nwztools/upgtools/Makefile @@ -31,7 +31,7 @@ endif CXXFLAGS+=-g -Wall $(DEFINES) CFLAGS+=-g -Wall -std=c99 $(DEFINES) -LDLIBS+=-lpthread +LDLIBS+=-lpthread -lz BINS=upgtool$(EXE_EXT) diff --git a/utils/nwztools/upgtools/make_release.sh b/utils/nwztools/upgtools/make_release.sh new file mode 100755 index 0000000000..2c72048767 --- /dev/null +++ b/utils/nwztools/upgtools/make_release.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +if [ $# -ne 1 ]; +then + echo "usage: $0 version" + exit 1 +fi + +ver="$1" +echo "version: $ver" + +echo "Building windows version..." +make PREFIX=i686-w64-mingw32- EXE_EXT=.exe clean && make PREFIX=i686-w64-mingw32- EXE_EXT=.exe && cp upgtool.exe upgtool-v${ver}.exe +echo "Building linux version..." +make clean && make && cp upgtool upgtool_64-v${ver} diff --git a/utils/nwztools/upgtools/upg.c b/utils/nwztools/upgtools/upg.c index 47b1acb07b..67de12455f 100644 --- a/utils/nwztools/upgtools/upg.c +++ b/utils/nwztools/upgtools/upg.c @@ -319,13 +319,6 @@ void *upg_write_memory(struct upg_file_t *file, const char *key, err_printf(GREY, "A UPG file must have at least one file\n"); return NULL; } - if(key_len == 16 && file->nr_files == 1) - { - err_printf(RED, "This will probably not work: the firmware updater for this device expects at least two files in the archive.\n"); - err_printf(RED, "The first one is a shell script and the second is a MD5 file. You can probably put whatever you want in this file,\n"); - err_printf(RED, "even make it empty, but it needs to be there.\n"); - /* let it run just in case */ - } bool is_v2 = false; size_t min_chunk_size, hdr_sz, ent_sz; @@ -337,7 +330,7 @@ void *upg_write_memory(struct upg_file_t *file, const char *key, } else if(key_len == 16) { - min_chunk_size = 16; + min_chunk_size = 4096; /* experimentally, V2 UPG files always seem to have 4k sizes */ hdr_sz = sizeof(struct upg_header_v2_t); ent_sz = sizeof(struct upg_entry_v2_t); is_v2 = true; @@ -348,6 +341,15 @@ void *upg_write_memory(struct upg_file_t *file, const char *key, return NULL; } + /* V2 wants at least two files, the second of which is supposed to contain MD5 sums */ + if(is_v2 && file->nr_files == 1) + { + err_printf(RED, "This will probably not work: the firmware updater for this device expects at least two files in the archive.\n"); + err_printf(RED, "The first one is a shell script and the second is a MD5 file. You can probably put whatever you want in this file,\n"); + err_printf(RED, "even make it empty, but it needs to be there.\n"); + /* let it run just in case */ + } + /* compute total size and create buffer */ size_t tot_hdr_siz = hdr_sz + file->nr_files * ent_sz; size_t tot_size = sizeof(struct upg_md5_t) + tot_hdr_siz; @@ -386,17 +388,6 @@ void *upg_write_memory(struct upg_file_t *file, const char *key, cprintf(RED, " %d\n", i); cprintf_field(" Offset: ", "0x%lx\n", offset); cprintf_field(" Size: ", "0x%lx\n", file->files[i].size); - if(!is_v2) - { - entry_v1[i].offset = offset; - entry_v1[i].size = file->files[i].size; - } - else - { - entry_v2[i].offset = offset; - entry_v2[i].size = file->files[i].size; - entry_v2[i].pad[0] = entry_v2[i].pad[1] = 0; - } /* copy data to buffer, with padding */ size_t r_size = ROUND_UP(file->files[i].size, min_chunk_size); void *data_ptr = (uint8_t *)buf + offset; @@ -413,6 +404,18 @@ void *upg_write_memory(struct upg_file_t *file, const char *key, aes_cbc_enc_set_key_iv((uint8_t *)key, (uint8_t *)g_aes_iv); aes_cbc_enc(data_ptr, r_size, data_ptr); } + /* write header */ + if(!is_v2) + { + entry_v1[i].offset = offset; + entry_v1[i].size = file->files[i].size; + } + else + { + entry_v2[i].offset = offset; + entry_v2[i].size = r_size; /* the V2 seems to write the padded size here */ + entry_v2[i].pad[0] = entry_v2[i].pad[1] = 0; + } offset += r_size; } diff --git a/utils/nwztools/upgtools/upgtool.c b/utils/nwztools/upgtools/upgtool.c index 81cb4aa939..8a9194ebe4 100644 --- a/utils/nwztools/upgtools/upgtool.c +++ b/utils/nwztools/upgtools/upgtool.c @@ -29,6 +29,7 @@ #include #include "misc.h" #include +#include #include "keysig_search.h" #include "upg.h" @@ -46,17 +47,11 @@ static char *g_kas = NULL; static char *g_key = NULL; static char *g_sig = NULL; static int g_nr_threads = 1; +#define MAX_NR_FILES 32 +bool g_compress[MAX_NR_FILES] = {false}; enum keysig_search_method_t g_keysig_search = KEYSIG_SEARCH_NONE; -#define let_the_force_flow(x) do { if(!g_force) return x; } while(0) -#define continue_the_force(x) if(x) let_the_force_flow(x) - -#define check_field(v_exp, v_have, str_ok, str_bad) \ - if((v_exp) != (v_have)) \ - { cprintf(RED, str_bad); let_the_force_flow(__LINE__); } \ - else { cprintf(RED, str_ok); } - #define cprintf(col, ...) do {color(col); printf(__VA_ARGS__); }while(0) #define cprintf_field(str1, ...) do{ cprintf(GREEN, str1); cprintf(YELLOW, __VA_ARGS__); }while(0) @@ -176,7 +171,51 @@ static int do_upg(void *buf, long size) continue; } free(str); - fwrite(file->files[i].data, 1, file->files[i].size, f); + if(g_compress[i]) + { + void *buf = file->files[i].data; + int size = file->files[i].size; + int pos = 0; + /* the fwpup tool seems to assume that every block decompresses to less than 4096 bytes, + * so I guess the encoder splits the input in chunks */ + int max_chunk_size = 4096; + void *chunk = malloc(max_chunk_size); + if(g_debug) + cprintf(GREY, "decompressing file %d with chunk size %d...\n", i, max_chunk_size); + while(pos + 4 <= size) + { + int compressed_chunk_size = *(uint32_t *)(buf + pos); + if(g_debug) + cprintf(GREY, "%d ", compressed_chunk_size); + if(compressed_chunk_size < 0) + { + cprintf(RED, "invalid block size when decompressing, something is wrong\n"); + break; + } + if(compressed_chunk_size == 0) + break; + uLongf chunk_size = max_chunk_size; + int zres = uncompress(chunk, &chunk_size, buf + pos + 4, compressed_chunk_size); + if(zres == Z_BUF_ERROR) + cprintf(RED, "the encoder produced a block greater than %d, I can't handle that\n", max_chunk_size); + if(zres == Z_DATA_ERROR) + cprintf(RED, "the compressed data seems corrupted\n"); + if(zres != Z_OK) + { + cprintf(RED, "the compressed suffered an error %d\n", zres); + break; + } + pos += 4 + compressed_chunk_size; + fwrite(chunk, 1, chunk_size, f); + } + free(chunk); + if(g_debug) + cprintf(GREY, "done."); + } + else + { + fwrite(file->files[i].data, 1, file->files[i].size, f); + } fclose(f); } upg_free(file); @@ -277,7 +316,40 @@ static int create_upg(int argc, char **argv) return 1; } fclose(f); - upg_append(upg, buf, size); + if(g_compress[i]) + { + int out_buf_max_sz = size; /* we expect that the output will not take more space */ + void *out_buf = malloc(out_buf_max_sz); + int out_buf_pos = 0, in_buf_pos = 0; + int max_chunk_size = 4096; /* the OF encoder/decoder expect that */ + while(in_buf_pos < size) + { + int chunk_size = MIN(size - in_buf_pos, max_chunk_size); + uLongf dest_len = out_buf_max_sz - out_buf_pos - 4; /* we reserve 4 for the size */ + int zres = compress(out_buf + out_buf_pos + 4, &dest_len, buf + in_buf_pos, chunk_size); + if(zres == Z_BUF_ERROR) + { + cprintf(RED, "the compresser produced a file greater than its input, I can't handle that\n"); + return 1; + } + else if(zres != Z_OK) + { + cprintf(RED, "the compresser suffered an error %d\n", zres); + return 1; + } + /* write output size in the output buffer */ + *(uint32_t *)(out_buf + out_buf_pos) = dest_len; + out_buf_pos += 4 + dest_len; + in_buf_pos += chunk_size; + } + /* add an extra zero-length chunk */ + *(uint32_t *)(out_buf + out_buf_pos) = 0; + out_buf_pos += 4; + upg_append(upg, out_buf, out_buf_pos); + free(buf); + } + else + upg_append(upg, buf, size); } size_t size = 0; @@ -325,6 +397,7 @@ static void usage(void) printf(" -s/--sig \tForce sig\n"); printf(" -e/--extract\t\tExtract a UPG archive\n"); printf(" -c/--create\t\tCreate a UPG archive\n"); + printf(" -z/--compress \t\t(De)compress file (starts at 0)\n"); printf("keysig search method:\n"); for(int i = KEYSIG_SEARCH_FIRST; i < KEYSIG_SEARCH_LAST; i++) printf(" %-10s\t%s\n", keysig_search_desc[i].name, keysig_search_desc[i].comment); @@ -355,10 +428,11 @@ int main(int argc, char **argv) {"extract", no_argument, 0, 'e'}, {"create", no_argument, 0 ,'c'}, {"threads", required_argument, 0, 't'}, + {"compress", required_argument, 0, 'z'}, {0, 0, 0, 0} }; - int c = getopt_long(argc, argv, "?dnfo:m:l:a:k:s:ect:", long_options, NULL); + int c = getopt_long(argc, argv, "?dnfo:m:l:a:k:s:ect:z:", long_options, NULL); if(c == -1) break; switch(c) @@ -418,6 +492,17 @@ int main(int argc, char **argv) return 1; } break; + case 'z': + { + int idx = strtol(optarg, NULL, 0); + if(idx < 0 || idx >= MAX_NR_FILES) + { + cprintf(GREY, "Invalid file index\n"); + return 1; + } + g_compress[idx] = true; + break; + } default: abort(); }