From: Mateusz Majewski Date: Tue, 31 May 2022 07:12:30 +0000 (+0200) Subject: Add upgrade-apply tool X-Git-Tag: accepted/tizen/unified/20220628.133556~19^2~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8d75f70141948bbe8bbbfc17648f54e03458b2b1;p=platform%2Fcore%2Fsystem%2Ftota-ua.git Add upgrade-apply tool This is a tool that allows us to apply an upgrade provided in a tar archive to a partition. Hopefully this will allow us to simplify the upgrade codebase a bit. Change-Id: Ibe876dd0141065e8c150a360b329f67ca6935c69 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 93fa8a1..3d1d51b 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,3 +113,4 @@ INSTALL(TARGETS ${BLKIDEXENAME} DESTINATION ${BINDIR}) ADD_SUBDIRECTORY(dmverity) ADD_SUBDIRECTORY(img-verifier) +ADD_SUBDIRECTORY(upgrade-apply) diff --git a/packaging/tota-ua.spec b/packaging/tota-ua.spec index a2dbaa3..97b5aa0 100755 --- a/packaging/tota-ua.spec +++ b/packaging/tota-ua.spec @@ -18,6 +18,7 @@ BuildRequires: pkgconfig(libtzplatform-config) BuildRequires: pkgconfig(dlog) Buildrequires: pkgconfig(glib-2.0) BuildRequires: pkgconfig(hal-api-device) +BuildRequires: libtar-devel Requires: tar Requires: gzip @@ -105,3 +106,6 @@ ln -s ../clone_partitions_recovery.service %{buildroot}%{_unitdir}/tizen-recover # blkid-print %{_bindir}/blkid-print + +# upgrade-apply +%{_bindir}/upgrade-apply diff --git a/upgrade-apply/CMakeLists.txt b/upgrade-apply/CMakeLists.txt new file mode 100644 index 0000000..e7cf40e --- /dev/null +++ b/upgrade-apply/CMakeLists.txt @@ -0,0 +1,16 @@ +find_package(PkgConfig) +pkg_check_modules(DEPS REQUIRED IMPORTED_TARGET + zlib + libbrotlidec +) + +set( + upgrade-apply_SRCS + main.c + ss_brotli_patch/ss_brotli_patch.c +) +add_executable(upgrade-apply ${upgrade-apply_SRCS}) +target_link_libraries(upgrade-apply PRIVATE PkgConfig::DEPS) +# Unfortunately, libtar is neither CMake- nor pkgconfig-aware. +target_link_libraries(upgrade-apply PRIVATE tar) +install(TARGETS upgrade-apply) diff --git a/upgrade-apply/main.c b/upgrade-apply/main.c new file mode 100644 index 0000000..63f13d9 --- /dev/null +++ b/upgrade-apply/main.c @@ -0,0 +1,395 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ss_brotli_patch/ss_brotli_patch.h" + +void fd_cleanup(int *fd) +{ + if (!fd || *fd < 0) + return; + close(*fd); +} + +void tar_cleanup(TAR **tar) +{ + if (!tar || !*tar) + return; + tar_close(*tar); +} + +enum archive_kind { + KIND_RAW, + KIND_BROTLI_PATCH, +}; + +struct parse_result { + enum { + PARSE_OK, + PARSE_HELP, + PARSE_REPEATED_ARGUMENT, + PARSE_MISSING_ARGUMENT, + PARSE_PATCH_MISTAKE, + PARSE_BAD_KIND, + PARSE_NO_PARSE, + } result; + + const char *archive; + const char *dest; + const char *archive_file; + const char *patch_orig; + enum archive_kind kind; +}; + +struct parse_result parse_args(int argc, char **argv) +{ + const char *archive = NULL; + const char *dest = NULL; + const char *archive_file = NULL; + const char *patch_orig = NULL; + enum archive_kind kind = -1; + bool help = false; + + for (;;) { + const struct option long_options[] = { + {"archive", required_argument, NULL, 0 }, + {"dest", required_argument, NULL, 1 }, + {"archive-file", required_argument, NULL, 2 }, + {"kind", required_argument, NULL, 3 }, + {"patch-orig", required_argument, NULL, 4 }, + {"help", no_argument, NULL, 'h'}, + {0} + }; + int option = getopt_long(argc, argv, "h", long_options, NULL); + if (option < 0) + break; + + switch (option) { + case 0: // archive + if (archive != NULL) + return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT }; + archive = optarg; + break; + + case 1: // dest + if (dest != NULL) + return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT }; + dest = optarg; + break; + + case 2: // archive-file + if (archive_file != NULL) + return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT }; + archive_file = optarg; + break; + + case 3: // kind + if (kind != -1) + return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT }; + if (strcmp(optarg, "raw") == 0) + kind = KIND_RAW; + else if (strcmp(optarg, "ss_brotli_patch") == 0) + kind = KIND_BROTLI_PATCH; + else + return (struct parse_result) { .result = PARSE_BAD_KIND }; + break; + + case 4: // patch-orig + if (patch_orig != NULL) + return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT }; + patch_orig = optarg; + break; + + case 'h': // help + if (help) + return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT }; + help = true; + break; + + default: + return (struct parse_result) { .result = PARSE_NO_PARSE }; + } + } + + if (help) { + if (archive != NULL || dest != NULL || archive_file != NULL || patch_orig != NULL || kind != -1) + return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT }; + return (struct parse_result) { .result = PARSE_HELP }; + } + + if (archive == NULL) + return (struct parse_result) { .result = PARSE_MISSING_ARGUMENT }; + if (dest == NULL) + return (struct parse_result) { .result = PARSE_MISSING_ARGUMENT }; + if (archive_file == NULL) + return (struct parse_result) { .result = PARSE_MISSING_ARGUMENT }; + if (kind == -1) + return (struct parse_result) { .result = PARSE_MISSING_ARGUMENT }; + + if (patch_orig == NULL && kind == KIND_BROTLI_PATCH) + return (struct parse_result) { .result = PARSE_PATCH_MISTAKE }; + if (patch_orig != NULL && kind == KIND_RAW) + return (struct parse_result) { .result = PARSE_PATCH_MISTAKE }; + + return (struct parse_result) { + .result = PARSE_OK, + .archive = archive, + .dest = dest, + .archive_file = archive_file, + .patch_orig = patch_orig, + .kind = kind, + }; +} + +// The TAR structure can only hold an int as a FD. +// libtar example uses some brutal hack which we don't want to do. +// We don't want to open multiple archive at the time, so let's just have 1 as FD, +// and instead hold the gzip structure globally. +static gzFile gzip_file; + +int gzip_open(const char *pathname, int oflags, ...) +{ + if (gzip_file != NULL) { + errno = EALREADY; + return -1; + } + + // We assume oflags == O_RDONLY, since that's what we actually use. + // Also we don't care about the mode since we are lazy. + if (oflags != O_RDONLY) { + errno = ENOTSUP; + return -1; + } + + __attribute__((cleanup(fd_cleanup))) int inner_fd = open(pathname, O_RDONLY); + if (inner_fd == -1) + return -1; + + gzip_file = gzopen(pathname, "r"); + if (gzip_file == NULL) + return -1; + inner_fd = -1; + + return 1; +} + +int gzip_close(__attribute__((unused)) int useless_fd) +{ + if (gzip_file == NULL) { + errno = EINVAL; + return -1; + } + + return gzclose(gzip_file); +} + +ssize_t gzip_read(__attribute__((unused)) int useless_fd, void *buf, size_t len) +{ + if (gzip_file == NULL) { + errno = EINVAL; + return -1; + } + + return gzread(gzip_file, buf, len); +} + +ssize_t gzip_write(__attribute__((unused)) int useless_fd, __attribute__((unused)) const void *buf, __attribute__((unused)) size_t len) +{ + // Again, we do not use this. + errno = ENOTSUP; + return -1; +} + +void help(char *name) +{ + fprintf(stderr, + "Usage: %s --archive ARCHIVE --archive-file FILE --dest DESTINATION --kind KIND [--patch-orig ORIGIN]\n" + "\n" + "Patches a partition using an archive.\n" + "It will look for a file named FILE in an archive called ARCHIVE; the archive should have an extension\n" + "of either .tar, .tar.gz or .tgz; it will be read in the way corresponding to the extension.\n" + "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" + " 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", + name); +} + +int apply_raw(const char *dest, TAR *tar) +{ + int size = th_get_size(tar); + + __attribute__((cleanup(fd_cleanup))) int out = open(dest, O_WRONLY); + if (out == -1) { + fprintf(stderr, "Couldn't open the target (errno: %m)\n"); + return -1; + } + + while (size > 0) { + char buf[T_BLOCKSIZE]; + int r_read = tar_block_read(tar, buf); + switch (r_read) { + case -1: + fprintf(stderr, "Couldn't read from the archive (errno: %m)\n"); + return -1; + case 0: + fprintf(stderr, "We have reached EOF unexpectedly\n"); + return -1; + } + + if (r_read > size) { + r_read = size; + } + size -= r_read; + + int written = 0; + while (written - r_read) { + int r_write = write(out, buf + written, r_read - written); + if (r_write == -1) { + fprintf(stderr, "Couldn't write to the partition (errno: %m)\n"); + return -1; + } + written += r_write; + } + } + + return 0; +} + +int main(int argc, char **argv) +{ + struct parse_result parsed = parse_args(argc, argv); + switch (parsed.result) { + case PARSE_REPEATED_ARGUMENT: + fprintf(stderr, "Argument incorrectly repeated\n"); + help(argv[0]); + return EXIT_FAILURE; + case PARSE_MISSING_ARGUMENT: + fprintf(stderr, "Argument incorrectly missing\n"); + help(argv[0]); + return EXIT_FAILURE; + case PARSE_PATCH_MISTAKE: + fprintf(stderr, "`patch-orig` parameter doesn't fit the kind\n"); + help(argv[0]); + return EXIT_FAILURE; + case PARSE_BAD_KIND: + fprintf(stderr, "Invalid `kind` parameter (possible: `raw`, `ss_brotli_patch`)\n"); + help(argv[0]); + return EXIT_FAILURE; + case PARSE_NO_PARSE: + fprintf(stderr, "Invalid parameters passed\n"); + help(argv[0]); + return EXIT_FAILURE; + case PARSE_HELP: + help(argv[0]); + return EXIT_SUCCESS; + case PARSE_OK: + break; + } + + tartype_t *type; + static tartype_t gzip_type = { gzip_open, gzip_close, gzip_read, gzip_write }; + if (strcmp(parsed.archive + strlen(parsed.archive) - strlen(".tar"), ".tar") == 0) + type = NULL; + else if (strcmp(parsed.archive + strlen(parsed.archive) - strlen(".tar.gz"), ".tar.gz") == 0) + type = &gzip_type; + else if (strcmp(parsed.archive + strlen(parsed.archive) - strlen(".tgz"), ".tgz") == 0) + type = &gzip_type; + else { + fprintf(stderr, "Unknown archive extension\n"); + help(argv[0]); + return EXIT_FAILURE; + } + + __attribute__ ((cleanup(tar_cleanup))) TAR *tar; + if (tar_open(&tar, parsed.archive, type, O_RDONLY, 0, 0) == -1) { + /* Usually we would instead initialize tar to NULL, and tar_open would not touch it in case of failure. + * Alas, that's not what happens; tar_open sets tar to a newly allocated address, does stuff there, + * and in case of failure frees the address but does not reset it. This means that in case of failure + * tar contains an invalid address. So we have to NULL it out (so tar_cleanup doesn't crash our program), + * and then we don't have to initialize tar to NULL in first place. */ + tar = NULL; + + fprintf(stderr, "Couldn't open the archive (errno: %m)\n"); + return EXIT_FAILURE; + } + + for (;;) { + switch (th_read(tar)) { + case -1: + fprintf(stderr, "Couldn't read the archive header (errno: %m)\n"); + return EXIT_FAILURE; + case 1: + // All files processed, which means... + fprintf(stderr, "File not found in archive\n"); + return EXIT_FAILURE; + case 0: + break; + } + + const char *this_name = th_get_pathname(tar); + if (this_name == NULL) { + // TODO: Can this even happen? I don't think docs say anything about this. + fprintf(stderr, "Couldn't extract the filename from the archive (malformed archive?)\n"); + return EXIT_FAILURE; + } + + if (strcmp(this_name, parsed.archive_file) != 0) { + if (TH_ISREG(tar)) { + if (tar_skip_regfile(tar) == -1) { + fprintf(stderr, "Couldn't skip an intermediate file from the archive (errno: %m; malformed archive?)\n"); + return EXIT_FAILURE; + } + } + continue; + } + + fprintf(stderr, "File found. Starting to write\n"); + + int r; + 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); + break; + } + + if (r != 0) { + fprintf(stderr, "Couldn't apply; you might need to restore from the backup!\n"); + return EXIT_FAILURE; + } + + fprintf(stderr, "Everything written successfully\n"); + return EXIT_SUCCESS; + } +} diff --git a/upgrade-apply/ss_brotli_patch/NOTES.md b/upgrade-apply/ss_brotli_patch/NOTES.md new file mode 100644 index 0000000..27abb13 --- /dev/null +++ b/upgrade-apply/ss_brotli_patch/NOTES.md @@ -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/ss_brotli_patch/ss_brotli_patch.c b/upgrade-apply/ss_brotli_patch/ss_brotli_patch.c index 6574d80..50227fc 100644 --- a/upgrade-apply/ss_brotli_patch/ss_brotli_patch.c +++ b/upgrade-apply/ss_brotli_patch/ss_brotli_patch.c @@ -1,5 +1,5 @@ /* - * libtota + * tota-ua (previously libtota) * * Copyright (c) 2022 Samsung Electronics Co., Ltd. * @@ -27,7 +27,7 @@ #include #include #include -#include "fota_log.h" +#include #define PF_OK 0 #define PF_ERROR_OPEN_FILE 1 @@ -35,7 +35,6 @@ #define PF_ERROR_INVALID_PATCH_FILE 3 #define PF_ERROR_DECOMPRESSION 4 -#define BUFF_IN_LEN 4096 #define BUFF_OUT_LEN 4096 #define SSINT_LEN 8 #define BLOCK_COUNT_REPORT 5000 @@ -43,13 +42,15 @@ const char SSDIFF_MAGIC[] = "SSDIFF40"; struct bs_data { - int src_fd, dest_fd, patch_fd; - void *src_ptr, *dest_ptr, *patch_ptr; + 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[BUFF_IN_LEN]; - unsigned char buff_out[BUFF_IN_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; @@ -64,19 +65,17 @@ static void free_data(struct bs_data *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->patch_ptr) munmap(data->patch_ptr, data->patch_len); if (data->src_fd) close(data->src_fd); - if (data->patch_fd) close(data->patch_fd); if (data->dest_fd) close(data->dest_fd); } -static int open_file(char *file_name, int mode) +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) - LOGE("Open file %s error: %m (%d)\n", file_name, errno); + fprintf(stderr, "Open file %s error: %m (%d)\n", file_name, errno); return fd; } @@ -98,65 +97,83 @@ 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 = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; - - 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) { - LOGE("Decoder error\n"); - return PF_ERROR_DECOMPRESSION; - } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) { - LOGE("Invalid source file\n"); - return PF_ERROR_DECOMPRESSION; + 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, char *source_file, size_t source_size, char *dest_file, size_t dest_size, char *patch_file) +static int open_files(struct bs_data *data, const char *source_file, const char *dest_file, TAR *patch_tar) { assert(data); assert(source_file); assert(dest_file); - assert(patch_file); + assert(patch_tar); data->src_fd = open_file(source_file, O_RDONLY); - data->patch_fd = open_file(patch_file, O_RDONLY); data->dest_fd = open_file(dest_file, O_RDWR); if (data->src_fd < 0 || - data->patch_fd < 0 || data->dest_fd < 0) return PF_ERROR_OPEN_FILE; - data->src_len = source_size; - data->patch_len = get_file_len(data->patch_fd); - data->dest_len = dest_size; + 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 = get_file_len(data->dest_fd); data->src_ptr = mmap(NULL, data->src_len, PROT_READ, MAP_PRIVATE, data->src_fd, 0); if (data->src_ptr == MAP_FAILED) { - LOGE("mmap source file error: %m (%d)", errno); - return PF_ERROR_MMAP; - } - - data->patch_ptr = mmap(NULL, data->patch_len, PROT_READ, MAP_PRIVATE, data->patch_fd, 0); - if (data->patch_ptr == MAP_FAILED) { - LOGE("mmap patch file error: %m (%d)", errno); + 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) { - LOGE("mmap destination error: %m (%d)\n", errno); + fprintf(stderr, "mmap destination error: %m (%d)\n", errno); return PF_ERROR_MMAP; } - data->compressed_pos = data->patch_ptr; - data->available_in = data->patch_len; + data->patch_remaining = data->patch_len; return PF_OK; } @@ -166,14 +183,14 @@ static void init_data(struct bs_data *data) assert(data); data->src_fd = -1; - data->patch_fd = -1; data->dest_fd = -1; data->src_ptr = NULL; data->dest_ptr = NULL; - data->patch_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; @@ -225,10 +242,10 @@ int read_header(struct bs_data *data, uint8_t **buff_out_pos) if (*buff_out_pos + sizeof(SSDIFF_MAGIC) > data->decompressed_pos || memcmp(data->buff_out, SSDIFF_MAGIC, sizeof(SSDIFF_MAGIC) - 1) != 0) { - LOGE("Invalid patch file\n"); + fprintf(stderr, "Invalid patch file\n"); return PF_ERROR_INVALID_PATCH_FILE; } else { - LOGL(LOG_SSENGINE, "Looks like SSDIFF\n"); + fprintf(stderr, "Looks like SSDIFF\n"); } *buff_out_pos += sizeof(SSDIFF_MAGIC) - 1; @@ -239,10 +256,10 @@ int read_header(struct bs_data *data, uint8_t **buff_out_pos) } size_t target_size = parse_ssint(*buff_out_pos); - LOGL(LOG_SSENGINE, "target_size: 0x%lx (%ld)\n", target_size, target_size); + fprintf(stderr, "target_size: 0x%lx (%ld)\n", target_size, target_size); if (target_size != data->dest_len) { - LOGE("Declared target size differs from that read from the patch\n"); + fprintf(stderr, "Declared target size differs from that read from the patch\n"); return PF_ERROR_INVALID_PATCH_FILE; } @@ -251,11 +268,11 @@ int read_header(struct bs_data *data, uint8_t **buff_out_pos) return PF_OK; } -int apply_patch_brotli(char *source_file, size_t source_size, char *dest_file, size_t dest_size, char *patch_file) +int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patch_tar) { assert(source_file); assert(dest_file); - assert(patch_file); + assert(patch_tar); int result; uint64_t blocks = 0; @@ -263,7 +280,7 @@ int apply_patch_brotli(char *source_file, size_t source_size, char *dest_file, s init_data(&data); - if ((result = open_files(&data, source_file, source_size, dest_file, dest_size, patch_file)) != PF_OK) + if ((result = open_files(&data, source_file, dest_file, patch_tar)) != PF_OK) goto exit; if ((result = decompress_bytes(&data, 0)) != PF_OK) @@ -342,14 +359,14 @@ int apply_patch_brotli(char *source_file, size_t source_size, char *dest_file, s blocks++; if (blocks % BLOCK_COUNT_REPORT == 0) { - printf("Number of processed patch blocks: %lld\n", blocks); + fprintf(stderr, "Number of processed patch blocks: %lu\n", blocks); } } result = PF_OK; exit: - printf("Total processed blocks: %lld\n", blocks); + 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 index 47694b9..7c5603c 100644 --- a/upgrade-apply/ss_brotli_patch/ss_brotli_patch.h +++ b/upgrade-apply/ss_brotli_patch/ss_brotli_patch.h @@ -1,5 +1,5 @@ /* - * libtota + * tota-ua (previously libtota) * * Copyright (c) 2022 Samsung Electronics Co., Ltd. * @@ -19,4 +19,6 @@ #include -extern int apply_patch_brotli(char *source_file, size_t source_size, char *dest_file, size_t dest_size, char *patch_file); +#include + +extern int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patch_tar);