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
--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" \
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})
#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)
enum archive_kind {
KIND_RAW,
KIND_BROTLI_PATCH,
+ KIND_LZMA_PATCH
};
struct parse_result {
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;
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 };
"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);
}
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) {
--- /dev/null
+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.
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+++ /dev/null
-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.
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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);