From 0756f19d8093a8b96aa9864843201ffe2a115373 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Sat, 4 Jun 2022 13:42:50 +0200 Subject: [PATCH] Add support for lzma patches (DELTA_IMG) Change-Id: I48db15713776faa1366e87666bdc96a79c1c61d3 --- scripts/upgrade-common.inc | 11 +- upgrade-apply/CMakeLists.txt | 6 +- upgrade-apply/main.c | 30 ++++- upgrade-apply/{ss_brotli_patch => patch}/NOTES.md | 0 upgrade-apply/patch/brotli.c | 49 +++++++ .../ss_brotli_patch.h => patch/brotli.h} | 8 +- upgrade-apply/patch/lzma.c | 143 +++++++++++++++++++++ upgrade-apply/patch/lzma.h | 23 ++++ .../ss_brotli_patch.c => patch/patch.c} | 71 ++++------ upgrade-apply/patch/patch.h | 58 +++++++++ 10 files changed, 337 insertions(+), 62 deletions(-) rename upgrade-apply/{ss_brotli_patch => patch}/NOTES.md (100%) create mode 100644 upgrade-apply/patch/brotli.c rename upgrade-apply/{ss_brotli_patch/ss_brotli_patch.h => patch/brotli.h} (81%) create mode 100644 upgrade-apply/patch/lzma.c create mode 100644 upgrade-apply/patch/lzma.h rename upgrade-apply/{ss_brotli_patch/ss_brotli_patch.c => patch/patch.c} (83%) create mode 100644 upgrade-apply/patch/patch.h diff --git a/scripts/upgrade-common.inc b/scripts/upgrade-common.inc index 94c83a1..54e0692 100644 --- a/scripts/upgrade-common.inc +++ b/scripts/upgrade-common.inc @@ -157,7 +157,7 @@ upgrade_images() { fi # We support only PRE_UA and DELTA_IMG_AB - if [[ ! "$TYPE" =~ ^(PRE_UA|DELTA_IMG_AB)$ ]]; then + if [[ ! "$TYPE" =~ ^(PRE_UA|DELTA_IMG_AB|DELTA_IMG)$ ]]; then PARTS_NAME_TO_UPDATE+=( "$PART_NAME:$TYPE" ) continue fi @@ -185,6 +185,15 @@ upgrade_images() { --kind raw \ --dest-sha1 "$NEW_SHA" && UP_RES=$? || UP_RES=$? ;; + DELTA_IMG) + "$FOTA_DIR/upgrade-apply" --archive "$DELTA_TAR" \ + --dest "$NEXT_PARTITION" \ + --dest-size "$NEW_SIZE" \ + --archive-file "$DELTA_NAME" \ + --kind ss_lzma_patch \ + --patch-orig "$CURR_PARTITION" \ + --dest-sha1 "$NEW_SHA" && UP_RES=$? || UP_RES=$? + ;; DELTA_IMG_AB) "$FOTA_DIR/upgrade-apply" --archive "$DELTA_TAR" \ --dest "$NEXT_PARTITION" \ diff --git a/upgrade-apply/CMakeLists.txt b/upgrade-apply/CMakeLists.txt index f92520a..d915e5b 100644 --- a/upgrade-apply/CMakeLists.txt +++ b/upgrade-apply/CMakeLists.txt @@ -1,10 +1,12 @@ find_package(PkgConfig) -pkg_check_modules(DEPS REQUIRED IMPORTED_TARGET zlib) +pkg_check_modules(DEPS REQUIRED IMPORTED_TARGET zlib liblzma-tool) set( upgrade-apply_SRCS main.c - ss_brotli_patch/ss_brotli_patch.c + patch/patch.c + patch/brotli.c + patch/lzma.c sha1/sha1.c ) add_executable(upgrade-apply ${upgrade-apply_SRCS}) diff --git a/upgrade-apply/main.c b/upgrade-apply/main.c index 4faff68..080a1cb 100644 --- a/upgrade-apply/main.c +++ b/upgrade-apply/main.c @@ -28,7 +28,9 @@ #include #include -#include "ss_brotli_patch/ss_brotli_patch.h" +#include "patch/patch.h" +#include "patch/brotli.h" +#include "patch/lzma.h" #include "sha1/sha1.h" void fd_cleanup(int *fd) @@ -48,6 +50,7 @@ void tar_cleanup(TAR **tar) enum archive_kind { KIND_RAW, KIND_BROTLI_PATCH, + KIND_LZMA_PATCH }; struct parse_result { @@ -124,6 +127,8 @@ struct parse_result parse_args(int argc, char **argv) kind = KIND_RAW; else if (strcmp(optarg, "ss_brotli_patch") == 0) kind = KIND_BROTLI_PATCH; + else if (strcmp(optarg, "ss_lzma_patch") == 0) + kind = KIND_LZMA_PATCH; else return (struct parse_result) { .result = PARSE_BAD_KIND }; break; @@ -174,7 +179,7 @@ struct parse_result parse_args(int argc, char **argv) if (kind == -1) return (struct parse_result) { .result = PARSE_MISSING_ARGUMENT }; - if (patch_orig == NULL && kind == KIND_BROTLI_PATCH) + if (patch_orig == NULL && (kind == KIND_BROTLI_PATCH || kind == KIND_LZMA_PATCH)) return (struct parse_result) { .result = PARSE_PATCH_MISTAKE }; if (patch_orig != NULL && kind == KIND_RAW) return (struct parse_result) { .result = PARSE_PATCH_MISTAKE }; @@ -261,11 +266,12 @@ void help(char *name) "The KIND parameter specifies how the file will be treated:\n" " - raw means that the file should be treated as an image to be written,\n" " - ss_brotli_patch means that the file should be treated as a delta in the (Tizen-specific) ss_brotli_patch\n" + " - ss_lzma_patch means that the file should be treated as a delta in the (Tizen-specific) ss_lzma_patch\n" " format. In this case, ORIGIN is needed and specifies the partition that a delta is based on.\n" "The results will be written to the DESTINATION partition.\n" "If SHA1 is provided (lowercase hexadecimal), it is compared to the one calculated on DESTINATION after writing.\n" "Additionally, if SIZE is provided, it is used as the destination size; this matters for SHA1 comparision,\n" - "and also if ss_brotli_patch is used.\n", + "and also if ss_brotli_patch or ss_lzma_patch is used.\n", name); } @@ -466,15 +472,27 @@ int main(int argc, char **argv) fprintf(stderr, "File found. Starting to write\n"); - int r; + int r = EXIT_FAILURE; + + switch (parsed.kind) { case KIND_RAW: r = apply_raw(parsed.dest, tar); break; + case KIND_BROTLI_PATCH: - r = apply_patch_brotli(parsed.patch_orig, parsed.dest, tar, parsed.dest_size); - break; + { + struct dec_funcs funcs = { init_brotli, decompress_brotli }; + r = apply_patch(parsed.patch_orig, parsed.dest, tar, parsed.dest_size, &funcs); + break; + } + case KIND_LZMA_PATCH: + { + struct dec_funcs funcs = { init_lzma, decompress_lzma, clean_lzma }; + r = apply_patch(parsed.patch_orig, parsed.dest, tar, parsed.dest_size, &funcs); + break; + } } if (r != 0) { diff --git a/upgrade-apply/ss_brotli_patch/NOTES.md b/upgrade-apply/patch/NOTES.md similarity index 100% rename from upgrade-apply/ss_brotli_patch/NOTES.md rename to upgrade-apply/patch/NOTES.md diff --git a/upgrade-apply/patch/brotli.c b/upgrade-apply/patch/brotli.c new file mode 100644 index 0000000..b5259bb --- /dev/null +++ b/upgrade-apply/patch/brotli.c @@ -0,0 +1,49 @@ +/* + * tota-ua (previously libtota) + * + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include "patch.h" + +int init_brotli(struct bs_data *data) +{ + assert(data); + data->state = BrotliDecoderCreateInstance(NULL, NULL, NULL); + return 0; +} + +int decompress_brotli(struct bs_data *data) +{ + assert(data); + BrotliDecoderState *bstate = (BrotliDecoderState*)data->state; + int result = BrotliDecoderDecompressStream(bstate, + &data->available_in, + &data->compressed_pos, + &data->available_out, + &data->decompressed_pos, + &data->total_size); + + switch (result) { + case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT: + return PF_NEED_MORE_DATA; + case BROTLI_DECODER_RESULT_ERROR: + return PF_ERROR_DECOMPRESSION; + } + return PF_OK; +} diff --git a/upgrade-apply/ss_brotli_patch/ss_brotli_patch.h b/upgrade-apply/patch/brotli.h similarity index 81% rename from upgrade-apply/ss_brotli_patch/ss_brotli_patch.h rename to upgrade-apply/patch/brotli.h index fb2fc95..919caac 100644 --- a/upgrade-apply/ss_brotli_patch/ss_brotli_patch.h +++ b/upgrade-apply/patch/brotli.h @@ -16,9 +16,7 @@ * limitations under the License. */ #pragma once +#include "patch.h" -#include - -#include - -extern int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size); +int init_brotli(struct bs_data *data); +int decompress_brotli(struct bs_data *data); diff --git a/upgrade-apply/patch/lzma.c b/upgrade-apply/patch/lzma.c new file mode 100644 index 0000000..60ea3bd --- /dev/null +++ b/upgrade-apply/patch/lzma.c @@ -0,0 +1,143 @@ +/* + * tota-ua (previously libtota) + * + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include +#include +#include "patch.h" +#include +#include + +#define HEADER_PLUS_SIZE_LEN LZMA_PROPS_SIZE+8 + +struct lzma_data { + bool initialized; + CLzmaDec *state; +}; + +static void *SzAlloc(void *p, size_t size) +{ + return MyAlloc(size); +} + + +static void SzFree(void *p, void *address) +{ + MyFree(address); +} + +ISzAlloc g_alloc = { SzAlloc, SzFree }; + +int init_lzma(struct bs_data *data) +{ + assert(data); + struct lzma_data *internal_data = (struct lzma_data*)malloc(sizeof(struct lzma_data)); + if (!internal_data) + return PF_ERROR_DECOMPRESSION; + + CLzmaDec *state = (CLzmaDec*)malloc(sizeof(CLzmaDec)); + if (!state) { + free(internal_data); + return PF_ERROR_DECOMPRESSION; + } + + data->state = internal_data; + internal_data->state = state; + internal_data->initialized = false; + + LzmaDec_Construct(state); + + return PF_OK; +} + +void clean_lzma(struct bs_data *data) +{ + assert(data); + if (!data->state) + return; + + struct lzma_data *internal_data = data->state; + LzmaDec_Free(internal_data->state, &g_alloc); + free(internal_data->state); + free(internal_data); + data->state = NULL; +} + +static int init_lzma_internal(struct bs_data *data) +{ + assert(data); + if (data->available_in < HEADER_PLUS_SIZE_LEN) + return PF_NEED_MORE_DATA; + struct lzma_data *internal_data = data->state; + + int res = LzmaDec_Allocate(internal_data->state, + data->compressed_pos, + LZMA_PROPS_SIZE, + &g_alloc); + if (res != SZ_OK) + return PF_ERROR_DECOMPRESSION; + + data->available_in -= HEADER_PLUS_SIZE_LEN; + data->compressed_pos += HEADER_PLUS_SIZE_LEN; + + LzmaDec_Init(internal_data->state); + internal_data->initialized = true; + return PF_OK; +} + +int decompress_lzma(struct bs_data *data) +{ + assert(data); + int res; + struct lzma_data *internal_data = data->state; + + + if (!internal_data->initialized) { + res = init_lzma_internal(data); + if (res != PF_OK) + return res; + } + + ELzmaStatus status; + size_t out_processed = data->available_out; + size_t in_processed = data->available_in; + + res = LzmaDec_DecodeToBuf(internal_data->state, + data->decompressed_pos, + &out_processed, + data->compressed_pos, + &in_processed, + LZMA_FINISH_ANY, + &status); + + data->available_out -= out_processed; + data->available_in -= in_processed; + data->decompressed_pos += out_processed; + data->compressed_pos += in_processed; + + if (res != SZ_OK) + return PF_ERROR_DECOMPRESSION; + + if (status == LZMA_STATUS_NEEDS_MORE_INPUT && data->available_in == 0) { + if (data->patch_remaining == 0) + return PF_OK; // Since we don't have anything to read, we're done. + return PF_NEED_MORE_DATA; + } + + return PF_OK; +} diff --git a/upgrade-apply/patch/lzma.h b/upgrade-apply/patch/lzma.h new file mode 100644 index 0000000..d527354 --- /dev/null +++ b/upgrade-apply/patch/lzma.h @@ -0,0 +1,23 @@ +/* + * tota-ua + * + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include "patch.h" + +int init_lzma(struct bs_data *data); +int decompress_lzma(struct bs_data *data); +void clean_lzma(struct bs_data *data); diff --git a/upgrade-apply/ss_brotli_patch/ss_brotli_patch.c b/upgrade-apply/patch/patch.c similarity index 83% rename from upgrade-apply/ss_brotli_patch/ss_brotli_patch.c rename to upgrade-apply/patch/patch.c index 4821fc4..55cbde2 100644 --- a/upgrade-apply/ss_brotli_patch/ss_brotli_patch.c +++ b/upgrade-apply/patch/patch.c @@ -21,48 +21,27 @@ #include #include #include -#include #include #include #include #include #include #include +#include "patch.h" -#define PF_OK 0 -#define PF_ERROR_OPEN_FILE 1 -#define PF_ERROR_MMAP 2 -#define PF_ERROR_INVALID_PATCH_FILE 3 -#define PF_ERROR_DECOMPRESSION 4 - -#define BUFF_OUT_LEN 4096 #define SSINT_LEN 8 #define BLOCK_COUNT_REPORT 5000 const char SSDIFF_MAGIC[] = "SSDIFF40"; -struct bs_data { - int src_fd, dest_fd; - void *src_ptr, *dest_ptr; - TAR *patch_tar; - size_t src_len, dest_len, patch_len; - unsigned char buff_in[T_BLOCKSIZE]; - unsigned char buff_out[BUFF_OUT_LEN]; - uint8_t *dest_pos; - uint8_t *src_pos; - size_t patch_remaining; - size_t available_in, available_out; - const uint8_t *compressed_pos; - uint8_t *decompressed_pos; - size_t total_size; - BrotliDecoderState *bstate; -}; - -static void free_data(struct bs_data *data) +static void free_data(struct bs_data *data, struct dec_funcs *funcs) { if (data == NULL) return; + if (funcs->clean) + funcs->clean(data); + if (data->src_ptr) munmap(data->src_ptr, data->src_len); if (data->dest_ptr) munmap(data->dest_ptr, data->dest_len); @@ -88,7 +67,7 @@ static size_t get_file_len(int fd) } -static size_t decompress_bytes(struct bs_data *data, size_t keep_offset) +static size_t decompress_bytes(struct dec_funcs *funcs, struct bs_data *data, size_t keep_offset) { assert(data); if (keep_offset > 0) { @@ -97,21 +76,15 @@ static size_t decompress_bytes(struct bs_data *data, size_t keep_offset) data->decompressed_pos = data->buff_out + keep_offset; data->available_out = sizeof(data->buff_out) - keep_offset; - BrotliDecoderResult result; + int result; for (;;) { - result = - BrotliDecoderDecompressStream(data->bstate, - &data->available_in, - &data->compressed_pos, - &data->available_out, - &data->decompressed_pos, - &data->total_size); - - if (result == BROTLI_DECODER_RESULT_ERROR) { + result = funcs->decompress(data); + + if (result == PF_ERROR_DECOMPRESSION) { fprintf(stderr, "Brotli decompression errored\n"); return PF_ERROR_DECOMPRESSION; - } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) { + } else if (result == PF_NEED_MORE_DATA) { if (data->patch_remaining <= 0) { fprintf(stderr, "We have ran out of data and decompression is still in progress\n"); return PF_ERROR_DECOMPRESSION; @@ -195,7 +168,7 @@ static void init_data(struct bs_data *data) data->compressed_pos = 0; data->available_out = 0; data->decompressed_pos = 0; - data->bstate = BrotliDecoderCreateInstance(NULL, NULL, NULL); + data->state = NULL; } static int64_t parse_ssint(unsigned char *buff) @@ -233,7 +206,7 @@ static int64_t parse_ssint(unsigned char *buff) return result; } -int read_header(struct bs_data *data, uint8_t **buff_out_pos) +int read_header(struct dec_funcs *funcs, struct bs_data *data, uint8_t **buff_out_pos) { assert(data); assert(buff_out_pos); @@ -251,7 +224,7 @@ int read_header(struct bs_data *data, uint8_t **buff_out_pos) *buff_out_pos += sizeof(SSDIFF_MAGIC) - 1; if (*buff_out_pos + SSINT_LEN > data->decompressed_pos) { - decompress_bytes(data, data->decompressed_pos - *buff_out_pos); + decompress_bytes(funcs, data, data->decompressed_pos - *buff_out_pos); *buff_out_pos = data->buff_out; } @@ -268,7 +241,7 @@ int read_header(struct bs_data *data, uint8_t **buff_out_pos) return PF_OK; } -int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size) +int apply_patch(const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size, struct dec_funcs *funcs) { assert(source_file); assert(dest_file); @@ -279,16 +252,18 @@ int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patc struct bs_data data; init_data(&data); + if ((result = funcs->init(&data)) != PF_OK) + goto exit; if ((result = open_files(&data, source_file, dest_file, patch_tar, dest_size)) != PF_OK) goto exit; - if ((result = decompress_bytes(&data, 0)) != PF_OK) + if ((result = decompress_bytes(funcs, &data, 0)) != PF_OK) goto exit; uint8_t *buff_out_pos; - if ((result = read_header(&data, &buff_out_pos)) != PF_OK) + if ((result = read_header(funcs, &data, &buff_out_pos)) != PF_OK) goto exit; uint64_t total_write = 0; @@ -298,7 +273,7 @@ int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patc * Make sure we can read the block header */ if (buff_out_pos + 4*8 > data.decompressed_pos) { - if ((result = decompress_bytes(&data, data.decompressed_pos - buff_out_pos)) != PF_OK) + if ((result = decompress_bytes(funcs, &data, data.decompressed_pos - buff_out_pos)) != PF_OK) goto exit; buff_out_pos = data.buff_out; } @@ -323,7 +298,7 @@ int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patc int64_t write = 0; while (write < diff_len) { if (buff_out_pos >= data.decompressed_pos) { - if ((result = decompress_bytes(&data, 0)) != PF_OK) + if ((result = decompress_bytes(funcs, &data, 0)) != PF_OK) goto exit; buff_out_pos = data.buff_out; } @@ -342,7 +317,7 @@ int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patc write = 0; while (write < extra_len) { if (buff_out_pos >= data.decompressed_pos) { - if ((result = decompress_bytes(&data, 0)) != PF_OK) + if ((result = decompress_bytes(funcs, &data, 0)) != PF_OK) goto exit; buff_out_pos = data.buff_out; } @@ -367,6 +342,6 @@ int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patc exit: fprintf(stderr, "Total processed blocks: %lu\n", blocks); - free_data(&data); + free_data(&data, funcs); return result; } diff --git a/upgrade-apply/patch/patch.h b/upgrade-apply/patch/patch.h new file mode 100644 index 0000000..08cf7eb --- /dev/null +++ b/upgrade-apply/patch/patch.h @@ -0,0 +1,58 @@ +/* + * tota-ua (previously libtota) + * + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include +#include +#include + +#define PF_OK 0 +#define PF_ERROR_OPEN_FILE 1 +#define PF_ERROR_MMAP 2 +#define PF_ERROR_INVALID_PATCH_FILE 3 +#define PF_ERROR_DECOMPRESSION 4 +#define PF_NEED_MORE_DATA 5 + +#define BUFF_OUT_LEN 4096 + +struct bs_data { + int src_fd, dest_fd; + void *src_ptr, *dest_ptr; + TAR *patch_tar; + size_t src_len, dest_len, patch_len; + unsigned char buff_in[T_BLOCKSIZE]; + unsigned char buff_out[BUFF_OUT_LEN]; + uint8_t *dest_pos; + uint8_t *src_pos; + size_t patch_remaining; + size_t available_in, available_out; + const uint8_t *compressed_pos; + uint8_t *decompressed_pos; + size_t total_size; + void *state; +}; + +struct dec_funcs { + int (*init)(struct bs_data *data); + int (*decompress)(struct bs_data *); + void (*clean)(struct bs_data *data); +}; + +extern int apply_patch(const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size, struct dec_funcs *funcs); -- 2.7.4