Add support for lzma patches (DELTA_IMG) 96/275896/10
authorMateusz Moscicki <m.moscicki2@partner.samsung.com>
Sat, 4 Jun 2022 11:42:50 +0000 (13:42 +0200)
committerMateusz Moscicki <m.moscicki2@partner.samsung.com>
Tue, 7 Jun 2022 14:38:46 +0000 (16:38 +0200)
Change-Id: I48db15713776faa1366e87666bdc96a79c1c61d3

13 files changed:
scripts/upgrade-common.inc
upgrade-apply/CMakeLists.txt
upgrade-apply/main.c
upgrade-apply/patch/NOTES.md [new file with mode: 0644]
upgrade-apply/patch/brotli.c [new file with mode: 0644]
upgrade-apply/patch/brotli.h [new file with mode: 0644]
upgrade-apply/patch/lzma.c [new file with mode: 0644]
upgrade-apply/patch/lzma.h [new file with mode: 0644]
upgrade-apply/patch/patch.c [new file with mode: 0644]
upgrade-apply/patch/patch.h [new file with mode: 0644]
upgrade-apply/ss_brotli_patch/NOTES.md [deleted file]
upgrade-apply/ss_brotli_patch/ss_brotli_patch.c [deleted file]
upgrade-apply/ss_brotli_patch/ss_brotli_patch.h [deleted file]

index 94c83a10a7f140d4e3a8ba85f5d8a4279b06049b..54e06922c66ba3ae8d5e1f2b263d5abe83598aa2 100644 (file)
@@ -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" \
index f92520a128cf98b25d39258067f3c9395c99ca14..d915e5b41bdd08e1453f0586a86e4f278e177251 100644 (file)
@@ -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})
index 4faff68c51662c6473176259c6d1ac1adcd9e713..080a1cbd124ef54d8190c9d872b9a4ce33474ac6 100644 (file)
@@ -28,7 +28,9 @@
 #include <libtar.h>
 #include <zlib.h>
 
-#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/patch/NOTES.md b/upgrade-apply/patch/NOTES.md
new file mode 100644 (file)
index 0000000..27abb13
--- /dev/null
@@ -0,0 +1,3 @@
+This is copied from platform/core/system/libtota @ b25d02d618797b5fb2fbe61a188d6a28dc720bf8.
+
+The code has been adapted to read patch from a tarball instead of a file descriptor, as well as some other minor improvements. It's mostly untouched otherwise.
diff --git a/upgrade-apply/patch/brotli.c b/upgrade-apply/patch/brotli.c
new file mode 100644 (file)
index 0000000..b5259bb
--- /dev/null
@@ -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 <stdio.h>
+#include <assert.h>
+#include <brotli/decode.h>
+#include <string.h>
+#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/patch/brotli.h b/upgrade-apply/patch/brotli.h
new file mode 100644 (file)
index 0000000..919caac
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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 "patch.h"
+
+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 (file)
index 0000000..60ea3bd
--- /dev/null
@@ -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 <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "patch.h"
+#include <LzmaDec.h>
+#include <Alloc.h>
+
+#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 (file)
index 0000000..d527354
--- /dev/null
@@ -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/patch/patch.c b/upgrade-apply/patch/patch.c
new file mode 100644 (file)
index 0000000..55cbde2
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * 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 <assert.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <libtar.h>
+#include "patch.h"
+
+#define SSINT_LEN 8
+#define BLOCK_COUNT_REPORT 5000
+
+const char SSDIFF_MAGIC[] = "SSDIFF40";
+
+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);
+
+    if (data->src_fd) close(data->src_fd);
+    if (data->dest_fd) close(data->dest_fd);
+}
+
+static int open_file(const char *file_name, int mode)
+{
+    assert(file_name);
+    int fd = open(file_name, mode, S_IWUSR | S_IRUSR);
+    if (fd < 0)
+        fprintf(stderr, "Open file %s error: %m (%d)\n", file_name, errno);
+    return fd;
+}
+
+static size_t get_file_len(int fd)
+{
+    assert(fd >= 0);
+    size_t result = lseek(fd, 0, SEEK_END);
+    lseek(fd, 0, SEEK_SET);
+    return result;
+}
+
+
+static size_t decompress_bytes(struct dec_funcs *funcs, struct bs_data *data, size_t keep_offset)
+{
+    assert(data);
+    if (keep_offset > 0) {
+        memcpy(data->buff_out, data->buff_out + sizeof(data->buff_out) - keep_offset, keep_offset);
+    }
+    data->decompressed_pos = data->buff_out + keep_offset;
+    data->available_out = sizeof(data->buff_out) - keep_offset;
+
+    int result;
+
+    for (;;) {
+        result = funcs->decompress(data);
+
+        if (result == PF_ERROR_DECOMPRESSION) {
+            fprintf(stderr, "Brotli decompression errored\n");
+            return PF_ERROR_DECOMPRESSION;
+        } 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;
+            }
+
+            int r_read = tar_block_read(data->patch_tar, data->buff_in);
+            switch (r_read) {
+            case -1:
+                fprintf(stderr, "Couldn't read from the archive (errno: %m)\n");
+                return PF_ERROR_DECOMPRESSION;
+            case 0:
+                fprintf(stderr, "We have reached EOF unexpectedly\n");
+                return PF_ERROR_DECOMPRESSION;
+            }
+
+            if (r_read > data->patch_remaining) {
+                r_read = data->patch_remaining;
+            }
+            data->patch_remaining -= r_read;
+
+            data->available_in = r_read;
+            data->compressed_pos = data->buff_in;
+        } else {
+            break;
+        }
+    }
+
+    return PF_OK;
+}
+
+static int open_files(struct bs_data *data, const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size)
+{
+    assert(data);
+    assert(source_file);
+    assert(dest_file);
+    assert(patch_tar);
+
+    data->src_fd = open_file(source_file, O_RDONLY);
+    data->dest_fd = open_file(dest_file, O_RDWR);
+    if (data->src_fd < 0 ||
+        data->dest_fd < 0)
+        return PF_ERROR_OPEN_FILE;
+
+    data->patch_tar = patch_tar;
+
+    data->src_len = get_file_len(data->src_fd);
+    data->patch_len = th_get_size(data->patch_tar);
+    data->dest_len = dest_size == 0 ? get_file_len(data->dest_fd) : dest_size;
+
+    data->src_ptr = mmap(NULL, data->src_len, PROT_READ, MAP_PRIVATE, data->src_fd, 0);
+    if (data->src_ptr == MAP_FAILED) {
+        fprintf(stderr, "mmap source file error: %m (%d)\n", errno);
+        return PF_ERROR_MMAP;
+    }
+
+    data->dest_ptr = mmap(NULL, data->dest_len, PROT_WRITE, MAP_SHARED, data->dest_fd, 0);
+    if (data->dest_ptr == MAP_FAILED) {
+        fprintf(stderr, "mmap destination error: %m (%d)\n", errno);
+        return PF_ERROR_MMAP;
+    }
+
+    data->patch_remaining = data->patch_len;
+
+    return PF_OK;
+}
+
+static void init_data(struct bs_data *data)
+{
+    assert(data);
+
+    data->src_fd = -1;
+    data->dest_fd = -1;
+    data->src_ptr = NULL;
+    data->dest_ptr = NULL;
+    data->patch_tar = NULL;
+    data->src_len = 0;
+    data->dest_len = 0;
+    data->patch_len = 0;
+    data->patch_remaining = 0;
+    data->available_in = 0;
+    data->compressed_pos = 0;
+    data->available_out = 0;
+    data->decompressed_pos = 0;
+    data->state = NULL;
+}
+
+static int64_t parse_ssint(unsigned char *buff)
+{
+    assert(buff);
+    /*
+     * From bsdiff 4.0 documentation:
+     *
+     * INTEGER type:
+     *
+     * offset   size   data type  value
+     * 0        1      byte       x0
+     * 1        1      byte       x1
+     * 2        1      byte       x2
+     * 3        1      byte       x3
+     * 4        1      byte       x4
+     * 5        1      byte       x5
+     * 6        1      byte       x6
+     * 7        1      byte       x7 + 128 * s
+     *
+     * The values x0, x2, x2, x3, x4, x5, x6 are between 0 and 255 (inclusive).
+     * The value x7 is between 0 and 127 (inclusive). The value s is 0 or 1.
+     *
+     * The INTEGER is parsed as:
+     * (x0 + x1 * 256 + x2 * 256^2 + x3 * 256^3 + x4 * 256^4 +
+     *     x5 * 256^5 + x6 * 256^6 + x7 * 256^7) * (-1)^s
+     *
+     * (In other words, an INTEGER is a 64-byte signed integer in sign-magnitude
+     * format, stored in little-endian byte order.)
+     */
+    int64_t result = *(int64_t*)buff & 0x7fffffff;
+    if ((buff[7] & 0x80) != 0)
+        result = -result;
+
+    return result;
+}
+
+int read_header(struct dec_funcs *funcs, struct bs_data *data, uint8_t **buff_out_pos)
+{
+    assert(data);
+    assert(buff_out_pos);
+
+    *buff_out_pos = data->buff_out;
+
+    if (*buff_out_pos + sizeof(SSDIFF_MAGIC) > data->decompressed_pos ||
+        memcmp(data->buff_out, SSDIFF_MAGIC, sizeof(SSDIFF_MAGIC) - 1) != 0) {
+        fprintf(stderr, "Invalid patch file\n");
+        return PF_ERROR_INVALID_PATCH_FILE;
+    } else {
+        fprintf(stderr, "Looks like SSDIFF\n");
+    }
+
+    *buff_out_pos += sizeof(SSDIFF_MAGIC) - 1;
+
+    if (*buff_out_pos + SSINT_LEN > data->decompressed_pos) {
+        decompress_bytes(funcs, data, data->decompressed_pos - *buff_out_pos);
+        *buff_out_pos = data->buff_out;
+    }
+
+    size_t target_size = parse_ssint(*buff_out_pos);
+    fprintf(stderr, "target_size: 0x%lx (%ld)\n", target_size, target_size);
+
+    if (target_size != data->dest_len) {
+        fprintf(stderr, "Declared target size differs from that read from the patch\n");
+        return PF_ERROR_INVALID_PATCH_FILE;
+    }
+
+    *buff_out_pos += SSINT_LEN;
+
+    return PF_OK;
+}
+
+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);
+    assert(patch_tar);
+
+    int result;
+    uint64_t blocks = 0;
+    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(funcs, &data, 0)) != PF_OK)
+        goto exit;
+
+    uint8_t *buff_out_pos;
+
+    if ((result = read_header(funcs, &data, &buff_out_pos)) != PF_OK)
+        goto exit;
+
+    uint64_t total_write = 0;
+
+    while (total_write < data.dest_len) {
+        /*
+         * Make sure we can read the block header
+         */
+        if (buff_out_pos + 4*8 > data.decompressed_pos) {
+            if ((result = decompress_bytes(funcs, &data, data.decompressed_pos - buff_out_pos)) != PF_OK)
+                goto exit;
+            buff_out_pos = data.buff_out;
+        }
+
+        /*
+         * Read the block header
+         */
+        int64_t diff_len = parse_ssint(buff_out_pos+0*8);
+        int64_t extra_len = parse_ssint(buff_out_pos+1*8);
+        int64_t old_pos = parse_ssint(buff_out_pos+2*8);
+        int64_t new_pos = parse_ssint(buff_out_pos+3*8);
+        buff_out_pos += 4*8;
+
+        /*
+         * Prepare pointers
+         */
+        data.dest_pos = data.dest_ptr + new_pos;
+        data.src_pos = data.src_ptr + old_pos;
+        /*
+         * Read diff data
+         */
+        int64_t write = 0;
+        while (write < diff_len) {
+            if (buff_out_pos >= data.decompressed_pos) {
+                if ((result = decompress_bytes(funcs, &data, 0)) != PF_OK)
+                    goto exit;
+                buff_out_pos = data.buff_out;
+            }
+            while (write < diff_len && buff_out_pos < data.decompressed_pos) {
+                *data.dest_pos = *(uint8_t*)(data.src_pos) + *(uint8_t*)buff_out_pos;
+                data.dest_pos++;
+                data.src_pos++;
+                buff_out_pos++;
+                write++;
+            }
+        }
+        total_write += write;
+        /*
+         * Read extra data
+         */
+        write = 0;
+        while (write < extra_len) {
+            if (buff_out_pos >= data.decompressed_pos) {
+                if ((result = decompress_bytes(funcs, &data, 0)) != PF_OK)
+                    goto exit;
+                buff_out_pos = data.buff_out;
+            }
+            int64_t chunk_size = extra_len - write;
+            if (buff_out_pos + chunk_size > data.decompressed_pos) {
+               chunk_size = data.decompressed_pos - buff_out_pos;
+            }
+            memcpy(data.dest_pos, buff_out_pos, chunk_size);
+            data.dest_pos += chunk_size;
+            buff_out_pos += chunk_size;
+            write += chunk_size;
+        }
+        total_write += write;
+
+        blocks++;
+        if (blocks % BLOCK_COUNT_REPORT == 0) {
+            fprintf(stderr, "Number of processed patch blocks: %lu\n", blocks);
+        }
+    }
+
+    result = PF_OK;
+
+exit:
+    fprintf(stderr, "Total processed blocks: %lu\n", blocks);
+    free_data(&data, funcs);
+    return result;
+}
diff --git a/upgrade-apply/patch/patch.h b/upgrade-apply/patch/patch.h
new file mode 100644 (file)
index 0000000..08cf7eb
--- /dev/null
@@ -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 <unistd.h>
+
+#include <libtar.h>
+#include <stdint.h>
+#include <sys/types.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 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);
diff --git a/upgrade-apply/ss_brotli_patch/NOTES.md b/upgrade-apply/ss_brotli_patch/NOTES.md
deleted file mode 100644 (file)
index 27abb13..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-This is copied from platform/core/system/libtota @ b25d02d618797b5fb2fbe61a188d6a28dc720bf8.
-
-The code has been adapted to read patch from a tarball instead of a file descriptor, as well as some other minor improvements. It's mostly untouched otherwise.
diff --git a/upgrade-apply/ss_brotli_patch/ss_brotli_patch.c b/upgrade-apply/ss_brotli_patch/ss_brotli_patch.c
deleted file mode 100644 (file)
index 4821fc4..0000000
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
- * 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 <assert.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <string.h>
-#include <errno.h>
-#include <brotli/decode.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sys/mman.h>
-#include <unistd.h>
-#include <libtar.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)
-{
-    if (data == NULL)
-        return;
-
-    if (data->src_ptr) munmap(data->src_ptr, data->src_len);
-    if (data->dest_ptr) munmap(data->dest_ptr, data->dest_len);
-
-    if (data->src_fd) close(data->src_fd);
-    if (data->dest_fd) close(data->dest_fd);
-}
-
-static int open_file(const char *file_name, int mode)
-{
-    assert(file_name);
-    int fd = open(file_name, mode, S_IWUSR | S_IRUSR);
-    if (fd < 0)
-        fprintf(stderr, "Open file %s error: %m (%d)\n", file_name, errno);
-    return fd;
-}
-
-static size_t get_file_len(int fd)
-{
-    assert(fd >= 0);
-    size_t result = lseek(fd, 0, SEEK_END);
-    lseek(fd, 0, SEEK_SET);
-    return result;
-}
-
-
-static size_t decompress_bytes(struct bs_data *data, size_t keep_offset)
-{
-    assert(data);
-    if (keep_offset > 0) {
-        memcpy(data->buff_out, data->buff_out + sizeof(data->buff_out) - keep_offset, keep_offset);
-    }
-    data->decompressed_pos = data->buff_out + keep_offset;
-    data->available_out = sizeof(data->buff_out) - keep_offset;
-
-    BrotliDecoderResult 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) {
-            fprintf(stderr, "Brotli decompression errored\n");
-            return PF_ERROR_DECOMPRESSION;
-        } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
-            if (data->patch_remaining <= 0) {
-                fprintf(stderr, "We have ran out of data and decompression is still in progress\n");
-                return PF_ERROR_DECOMPRESSION;
-            }
-
-            int r_read = tar_block_read(data->patch_tar, data->buff_in);
-            switch (r_read) {
-            case -1:
-                fprintf(stderr, "Couldn't read from the archive (errno: %m)\n");
-                return PF_ERROR_DECOMPRESSION;
-            case 0:
-                fprintf(stderr, "We have reached EOF unexpectedly\n");
-                return PF_ERROR_DECOMPRESSION;
-            }
-
-            if (r_read > data->patch_remaining) {
-                r_read = data->patch_remaining;
-            }
-            data->patch_remaining -= r_read;
-
-            data->available_in = r_read;
-            data->compressed_pos = data->buff_in;
-        } else {
-            break;
-        }
-    }
-
-    return PF_OK;
-}
-
-static int open_files(struct bs_data *data, const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size)
-{
-    assert(data);
-    assert(source_file);
-    assert(dest_file);
-    assert(patch_tar);
-
-    data->src_fd = open_file(source_file, O_RDONLY);
-    data->dest_fd = open_file(dest_file, O_RDWR);
-    if (data->src_fd < 0 ||
-        data->dest_fd < 0)
-        return PF_ERROR_OPEN_FILE;
-
-    data->patch_tar = patch_tar;
-
-    data->src_len = get_file_len(data->src_fd);
-    data->patch_len = th_get_size(data->patch_tar);
-    data->dest_len = dest_size == 0 ? get_file_len(data->dest_fd) : dest_size;
-
-    data->src_ptr = mmap(NULL, data->src_len, PROT_READ, MAP_PRIVATE, data->src_fd, 0);
-    if (data->src_ptr == MAP_FAILED) {
-        fprintf(stderr, "mmap source file error: %m (%d)\n", errno);
-        return PF_ERROR_MMAP;
-    }
-
-    data->dest_ptr = mmap(NULL, data->dest_len, PROT_WRITE, MAP_SHARED, data->dest_fd, 0);
-    if (data->dest_ptr == MAP_FAILED) {
-        fprintf(stderr, "mmap destination error: %m (%d)\n", errno);
-        return PF_ERROR_MMAP;
-    }
-
-    data->patch_remaining = data->patch_len;
-
-    return PF_OK;
-}
-
-static void init_data(struct bs_data *data)
-{
-    assert(data);
-
-    data->src_fd = -1;
-    data->dest_fd = -1;
-    data->src_ptr = NULL;
-    data->dest_ptr = NULL;
-    data->patch_tar = NULL;
-    data->src_len = 0;
-    data->dest_len = 0;
-    data->patch_len = 0;
-    data->patch_remaining = 0;
-    data->available_in = 0;
-    data->compressed_pos = 0;
-    data->available_out = 0;
-    data->decompressed_pos = 0;
-    data->bstate = BrotliDecoderCreateInstance(NULL, NULL, NULL);
-}
-
-static int64_t parse_ssint(unsigned char *buff)
-{
-    assert(buff);
-    /*
-     * From bsdiff 4.0 documentation:
-     *
-     * INTEGER type:
-     *
-     * offset   size   data type  value
-     * 0        1      byte       x0
-     * 1        1      byte       x1
-     * 2        1      byte       x2
-     * 3        1      byte       x3
-     * 4        1      byte       x4
-     * 5        1      byte       x5
-     * 6        1      byte       x6
-     * 7        1      byte       x7 + 128 * s
-     *
-     * The values x0, x2, x2, x3, x4, x5, x6 are between 0 and 255 (inclusive).
-     * The value x7 is between 0 and 127 (inclusive). The value s is 0 or 1.
-     *
-     * The INTEGER is parsed as:
-     * (x0 + x1 * 256 + x2 * 256^2 + x3 * 256^3 + x4 * 256^4 +
-     *     x5 * 256^5 + x6 * 256^6 + x7 * 256^7) * (-1)^s
-     *
-     * (In other words, an INTEGER is a 64-byte signed integer in sign-magnitude
-     * format, stored in little-endian byte order.)
-     */
-    int64_t result = *(int64_t*)buff & 0x7fffffff;
-    if ((buff[7] & 0x80) != 0)
-        result = -result;
-
-    return result;
-}
-
-int read_header(struct bs_data *data, uint8_t **buff_out_pos)
-{
-    assert(data);
-    assert(buff_out_pos);
-
-    *buff_out_pos = data->buff_out;
-
-    if (*buff_out_pos + sizeof(SSDIFF_MAGIC) > data->decompressed_pos ||
-        memcmp(data->buff_out, SSDIFF_MAGIC, sizeof(SSDIFF_MAGIC) - 1) != 0) {
-        fprintf(stderr, "Invalid patch file\n");
-        return PF_ERROR_INVALID_PATCH_FILE;
-    } else {
-        fprintf(stderr, "Looks like SSDIFF\n");
-    }
-
-    *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);
-        *buff_out_pos = data->buff_out;
-    }
-
-    size_t target_size = parse_ssint(*buff_out_pos);
-    fprintf(stderr, "target_size: 0x%lx (%ld)\n", target_size, target_size);
-
-    if (target_size != data->dest_len) {
-        fprintf(stderr, "Declared target size differs from that read from the patch\n");
-        return PF_ERROR_INVALID_PATCH_FILE;
-    }
-
-    *buff_out_pos += SSINT_LEN;
-
-    return PF_OK;
-}
-
-int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size)
-{
-    assert(source_file);
-    assert(dest_file);
-    assert(patch_tar);
-
-    int result;
-    uint64_t blocks = 0;
-    struct bs_data data;
-
-    init_data(&data);
-
-    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)
-        goto exit;
-
-    uint8_t *buff_out_pos;
-
-    if ((result = read_header(&data, &buff_out_pos)) != PF_OK)
-        goto exit;
-
-    uint64_t total_write = 0;
-
-    while (total_write < data.dest_len) {
-        /*
-         * 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)
-                goto exit;
-            buff_out_pos = data.buff_out;
-        }
-
-        /*
-         * Read the block header
-         */
-        int64_t diff_len = parse_ssint(buff_out_pos+0*8);
-        int64_t extra_len = parse_ssint(buff_out_pos+1*8);
-        int64_t old_pos = parse_ssint(buff_out_pos+2*8);
-        int64_t new_pos = parse_ssint(buff_out_pos+3*8);
-        buff_out_pos += 4*8;
-
-        /*
-         * Prepare pointers
-         */
-        data.dest_pos = data.dest_ptr + new_pos;
-        data.src_pos = data.src_ptr + old_pos;
-        /*
-         * Read diff data
-         */
-        int64_t write = 0;
-        while (write < diff_len) {
-            if (buff_out_pos >= data.decompressed_pos) {
-                if ((result = decompress_bytes(&data, 0)) != PF_OK)
-                    goto exit;
-                buff_out_pos = data.buff_out;
-            }
-            while (write < diff_len && buff_out_pos < data.decompressed_pos) {
-                *data.dest_pos = *(uint8_t*)(data.src_pos) + *(uint8_t*)buff_out_pos;
-                data.dest_pos++;
-                data.src_pos++;
-                buff_out_pos++;
-                write++;
-            }
-        }
-        total_write += write;
-        /*
-         * Read extra data
-         */
-        write = 0;
-        while (write < extra_len) {
-            if (buff_out_pos >= data.decompressed_pos) {
-                if ((result = decompress_bytes(&data, 0)) != PF_OK)
-                    goto exit;
-                buff_out_pos = data.buff_out;
-            }
-            int64_t chunk_size = extra_len - write;
-            if (buff_out_pos + chunk_size > data.decompressed_pos) {
-               chunk_size = data.decompressed_pos - buff_out_pos;
-            }
-            memcpy(data.dest_pos, buff_out_pos, chunk_size);
-            data.dest_pos += chunk_size;
-            buff_out_pos += chunk_size;
-            write += chunk_size;
-        }
-        total_write += write;
-
-        blocks++;
-        if (blocks % BLOCK_COUNT_REPORT == 0) {
-            fprintf(stderr, "Number of processed patch blocks: %lu\n", blocks);
-        }
-    }
-
-    result = PF_OK;
-
-exit:
-    fprintf(stderr, "Total processed blocks: %lu\n", blocks);
-    free_data(&data);
-    return result;
-}
diff --git a/upgrade-apply/ss_brotli_patch/ss_brotli_patch.h b/upgrade-apply/ss_brotli_patch/ss_brotli_patch.h
deleted file mode 100644 (file)
index fb2fc95..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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 <unistd.h>
-
-#include <libtar.h>
-
-extern int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size);