Report upgrade progress for DELTA_IMAGE type deltas 86/309186/4
authorAntoni <a.adaszkiewi@samsung.com>
Thu, 4 Apr 2024 11:23:09 +0000 (13:23 +0200)
committerAntoni <a.adaszkiewi@samsung.com>
Thu, 23 May 2024 11:46:28 +0000 (13:46 +0200)
This requires a new file (update-progress-info.ini) in archive.

Change-Id: I8108bb69a1fd0e2bb41548c1003218f99df6b829

scripts/upgrade-support/upgrade-common.inc
scripts/upgrade-support/upgrade-partial.sh
src/upgrade-apply/main.c
src/upgrade-apply/patch/patch.c
src/upgrade-apply/patch/patch.h
src/upgrade-apply/patch/patch_helper.c

index 5b1455ed0c9708cb8af354e91d4fd6b6f073e65e..3b304b82dc8ea83cfd1fbed20751970c329def05 100644 (file)
@@ -83,6 +83,19 @@ retrap() {
        trap 'echo "Aborting due to errexit on ${0##*/}:$LINENO. Exit code: $?" >&2' ERR
 }
 
+check_if_file_in_archive() {
+       ARCHIVE_NAME="$1"
+       FILE_NAME="$2"
+       local RET=0
+       tar tf "${ARCHIVE_NAME}" "${FILE_NAME}" >/dev/null 2>&1 || RET=$?
+
+       if [ $RET -eq 0 ]; then
+               return $TRUE
+       else
+               return $FALSE
+       fi
+}
+
 verify_file_internal() {
        FILE_NAME="$1"
        FILE_PATH="$FOTA_DIR/$FILE_NAME"
@@ -399,6 +412,14 @@ upgrade_images() {
                declare "LABEL_MAP_${LABEL}=${PARTLABEL}"
        done < <(sed -e '/^#/d' -e '/^$/d' "${LABEL_MAP_PATH}/${HAL_PART_MAP_FILE}")
 
+       local PROGRESS_INFO_FILE_PATH="$2"
+       local PROGRESS_INFO_FILE_ARG
+       if [ -n "$PROGRESS_INFO_FILE_PATH" ]; then
+               PROGRESS_INFO_FILE_ARG="--progress-info-file $PROGRESS_INFO_FILE_PATH"
+       else
+               PROGRESS_INFO_FILE_ARG=""
+       fi
+
        # _OFFSET _SIZE _HASH1 _HASH2
        while read -r LABEL_NAME DELTA_NAME TYPE DEV OFFSET OLD_SIZE NEW_SIZE OLD_SHA NEW_SHA
        do
@@ -458,7 +479,8 @@ upgrade_images() {
                                                                        `"--kind ss_brotli_patch "`
                                                                        `"--patch-orig $CURR_PARTITION "`
                                                                        `"--no-write-all "`
-                                                                       `"--dest-sha1 $NEW_SHA"
+                                                                       `"--dest-sha1 $NEW_SHA "`
+                                                                       `"$PROGRESS_INFO_FILE_ARG"
                                UP_RES=$?
                                ;;
                        DELTA_FS)
index 1b3d2d010177c8b53ece0ec378d531924df79a34..22826a9e5fc22defbc12b3b446052982c43288e0 100755 (executable)
@@ -44,11 +44,20 @@ prepare_fota_update_dir
 check_ab_partition_scheme
 check_used_block_device
 check_args "$@"
+
 unpack_file "$DELTA_TAR" "upgrade-apply"
 /bin/chmod +x "$FOTA_DIR/upgrade-apply"
 unpack_file "$DELTA_TAR" "upgrade-apply-deltafs"
 /bin/chmod +x "$FOTA_DIR/upgrade-apply-deltafs"
-if ! upgrade_images "$DELTA_TAR"; then
+
+if check_if_file_in_archive "$DELTA_TAR" "update-progress-info.ini"; then
+       unpack_file "$DELTA_TAR" "update-progress-info.ini"
+       PROGRESS_INFO_FILE_PATH="$FOTA_DIR/update-progress-info.ini"
+else
+       PROGRESS_INFO_FILE_PATH=""
+fi
+
+if ! upgrade_images "$DELTA_TAR" "$PROGRESS_INFO_FILE_PATH"; then
        critical_log "[Error] Unable to upgrade_images"
        cleanup
        exit_error
index d8fbe9bc34cd1a1b6f482ac8eb8f1e122d15346e..343e4bce1f89dd12f158a12e635e9b83d7ffec73 100644 (file)
@@ -58,6 +58,7 @@ struct parse_result {
                PARSE_REPEATED_ARGUMENT,
                PARSE_MISSING_ARGUMENT,
                PARSE_PATCH_MISTAKE,
+               PARSE_PROGRESS_BAD_KIND,
                PARSE_BAD_KIND,
                PARSE_BAD_SIZE,
                PARSE_NO_PARSE,
@@ -68,6 +69,7 @@ struct parse_result {
        const char *archive_file;
        const char *patch_orig;
        const char *dest_sha1;
+       const char *progress_info_file;
        size_t dest_size;
        bool write_all;
        enum archive_kind kind;
@@ -80,6 +82,7 @@ struct parse_result parse_args(int argc, char **argv)
        const char *archive_file = NULL;
        const char *patch_orig = NULL;
        const char *dest_sha1 = NULL;
+       const char *progress_info_file = NULL;
        long long int dest_size = 0;
        enum archive_kind kind = -1;
        bool help = false;
@@ -87,15 +90,16 @@ struct parse_result parse_args(int argc, char **argv)
 
        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  },
-                       {"dest-sha1",    required_argument, NULL, 5  },
-                       {"dest-size",    required_argument, NULL, 6  },
-                       {"no-write-all", no_argument,       NULL, 'n'},
-                       {"help",         no_argument,       NULL, 'h'},
+                       {"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  },
+                       {"dest-sha1",          required_argument, NULL, 5  },
+                       {"dest-size",          required_argument, NULL, 6  },
+                       {"progress-info-file", required_argument, NULL, 7  },
+                       {"no-write-all",       no_argument,       NULL, 'n'},
+                       {"help",               no_argument,       NULL, 'h'},
                        {0}
                };
                int option = getopt_long(argc, argv, "hn", long_options, NULL);
@@ -152,6 +156,12 @@ struct parse_result parse_args(int argc, char **argv)
                                return (struct parse_result) { .result = PARSE_BAD_SIZE };
                        break;
 
+               case 7: // progress-info-file
+                       if (progress_info_file != NULL)
+                               return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT };
+                       progress_info_file = optarg;
+                       break;
+
                case 'n': // no-write-all
                        if (!write_all)
                                return (struct parse_result) { .result = PARSE_REPEATED_ARGUMENT };
@@ -189,6 +199,9 @@ struct parse_result parse_args(int argc, char **argv)
        if (patch_orig != NULL && kind == KIND_RAW)
                return (struct parse_result) { .result = PARSE_PATCH_MISTAKE };
 
+       if (progress_info_file != NULL && kind == KIND_RAW)
+               return (struct parse_result) { .result = PARSE_PROGRESS_BAD_KIND };
+
        return (struct parse_result) {
                .result = PARSE_OK,
                .archive = archive,
@@ -197,6 +210,7 @@ struct parse_result parse_args(int argc, char **argv)
                .patch_orig = patch_orig,
                .dest_sha1 = dest_sha1,
                .dest_size = dest_size,
+               .progress_info_file = progress_info_file,
                .kind = kind,
                .write_all = write_all,
        };
@@ -259,7 +273,8 @@ ssize_t gzip_write(__attribute__((unused)) int useless_fd, __attribute__((unused
 void help(char *name)
 {
        fprintf(stderr,
-               "Usage: %s --archive ARCHIVE --archive-file FILE --dest DESTINATION --kind KIND [--patch-orig ORIGIN] [--dest-sha1 SHA1] [--dest-size SIZE] [--no-write-all]\n"
+               "Usage: %s --archive ARCHIVE --archive-file FILE --dest DESTINATION --kind KIND \
+                       [--patch-orig ORIGIN] [--dest-sha1 SHA1] [--dest-size SIZE] [--no-write-all] [--progress-info-file FILE]\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"
@@ -273,7 +288,8 @@ void help(char *name)
                "Additionally, if SIZE is provided, it is used as the destination size; this matters for SHA1 comparision,\n"
                "and also if ss_brotli_patch.\n"
                "In case the KIND is ss_brotli_path and the DESTINATION is the clone of the ORIGIN you can specify\n"
-               "the --no-write-all flag to not copy the data from the source, because it is already there.\n",
+               "the --no-write-all flag to not copy the data from the source, because it is already there.\n"
+               "Progress info file can be provided for deltas using ss_brotli_patch to report upgrade progress. The file contains block count of the delta.\n",
        name);
 }
 
@@ -380,6 +396,79 @@ while_done:;
        return 0;
 }
 
+int get_block_count_from_progress_file(const char* progress_info_file, const char *archive_file, uint64_t *block_count)
+{
+       const char *separators = " =\n";
+       FILE *fp = NULL;
+       char *buf = NULL;
+       int ret = 0;
+
+       fp = fopen(progress_info_file, "r");
+       if (fp == NULL) {
+               fprintf(stderr, "Failed to open %s\n", progress_info_file);
+               return -1;
+       }
+
+       fseek(fp, 0, SEEK_END);
+       off_t file_size = ftello(fp);
+       if (file_size == -1) {
+               fprintf(stderr, "Failed to check size of %s\n", progress_info_file);
+               ret = -1;
+               goto cleanup;
+       }
+       fseek(fp, 0 , SEEK_SET);
+
+       buf = (char *)malloc(file_size + 1);
+       if (buf == NULL) {
+               fprintf(stderr, "Malloc error\n");
+               ret = -1;
+               goto cleanup;
+       }
+
+       if (fread(buf, 1, file_size, fp) != file_size) {
+               fprintf(stderr, "Failed to read %s\n", progress_info_file);
+               ret = -1;
+               goto cleanup;
+       }
+
+       buf[file_size] = '\0';
+       int iter = 0;
+       char *saveptr;
+       char *read_word = strtok_r(buf, separators, &saveptr);
+
+       while (read_word) {
+               if (iter % 2 == 0) {
+                       if (strcmp(read_word, archive_file) == 0) {
+                               read_word = strtok_r(NULL, separators, &saveptr);
+
+                               if (read_word == NULL) {
+                                       fprintf(stderr, "%s file is not compatible - value missing\n", progress_info_file);
+                                       ret = -1;
+                               } else {
+                                       *block_count = (uint64_t) atoi(read_word);
+                                       fprintf(stderr, "Block count for %s: %ld\n", archive_file, *block_count);
+                                       ret = 0;
+                               }
+
+                               goto cleanup;
+                       }
+               }
+               iter++;
+               read_word = strtok_r(NULL, separators, &saveptr);
+       }
+
+       fprintf(stderr, "Block count for %s was not found in %s\n", archive_file, progress_info_file);
+       ret = -1;
+
+cleanup:
+       if (fp != NULL)
+               fclose(fp);
+       if (buf != NULL)
+               free(buf);
+
+       return ret;
+}
+
 int main(int argc, char **argv)
 {
        struct parse_result parsed = parse_args(argc, argv);
@@ -396,6 +485,10 @@ int main(int argc, char **argv)
                fprintf(stderr, "`patch-orig` parameter doesn't fit the kind\n");
                help(argv[0]);
                return EXIT_FAILURE;
+       case PARSE_PROGRESS_BAD_KIND:
+               fprintf(stderr, "`progress-info-file` parameter is only supported for ss_brotli_patch\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]);
@@ -485,8 +578,21 @@ int main(int argc, char **argv)
 
                case KIND_BROTLI_PATCH:
                        {
+                               uint64_t block_count;
+                               uint64_t *block_count_ptr = &block_count;
+                               if (parsed.progress_info_file != NULL) {
+                                       if (get_block_count_from_progress_file(parsed.progress_info_file,
+                                               parsed.archive_file, block_count_ptr) != 0) {
+                                                       fprintf(stderr, "Failed to get block count for %s\n", parsed.archive_file);
+                                                       return EXIT_FAILURE;
+                                               }
+                               } else {
+                                       block_count_ptr = NULL;
+                               }
+
                                struct dec_funcs funcs = { init_brotli, decompress_brotli, free_brotli };
-                               r = apply_patch(parsed.patch_orig, parsed.dest, tar, parsed.dest_size, NULL, parsed.write_all, &funcs);
+                               r = apply_patch(parsed.patch_orig, parsed.dest, tar, parsed.dest_size, NULL,
+                                       parsed.write_all, &funcs, block_count_ptr);
                                break;
                        }
                }
index 909007cd84a3a3528b53fd3f62f372e803667fb8..9fac82ad6d752c500e07cc01999355b177aa3a09 100644 (file)
@@ -42,6 +42,8 @@
 #define SSINT_LEN 8
 #define BLOCK_COUNT_REPORT 5000
 
+#define PROGRESS_REPORT_FREQUENCY 300
+
 const char SSDIFF_MAGIC[] = "SSDIFF40";
 
 static void free_data(struct bs_data *data, struct dec_funcs *funcs)
@@ -342,7 +344,8 @@ int read_header(struct dec_funcs *funcs, struct bs_data *data, uint8_t **buff_ou
     return PF_OK;
 }
 
-int apply_patch(const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size, size_t *free_space, bool write_all, struct dec_funcs *funcs)
+int apply_patch(const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size,
+    size_t *free_space, bool write_all, struct dec_funcs *funcs, uint64_t *block_count)
 {
     assert(source_file);
     assert(dest_file);
@@ -374,6 +377,15 @@ int apply_patch(const char *source_file, const char *dest_file, TAR *patch_tar,
     if ((res = remmap(&data.dest, 0)) != PF_OK)
         return res;
 
+    uint64_t progress_report_block_count = 0;
+    if (block_count != NULL) {
+        if (*block_count < PROGRESS_REPORT_FREQUENCY) {
+            progress_report_block_count = *block_count;
+        } else {
+            progress_report_block_count = *block_count / PROGRESS_REPORT_FREQUENCY;
+        }
+    }
+
     uint64_t total_write = 0;
 
     while (total_write < data.dest.len) {
@@ -457,11 +469,23 @@ int apply_patch(const char *source_file, const char *dest_file, TAR *patch_tar,
         total_write += write;
 
         blocks++;
-        if (blocks % BLOCK_COUNT_REPORT == 0) {
-            fprintf(stderr, "Number of processed patch blocks: %"PRIu64"\n", blocks);
+
+        if (block_count != NULL) {
+            if (blocks % progress_report_block_count == 0) {
+                fprintf(stderr, "\rUpgrade progress: %.2f%%", (double) blocks / *block_count * 100);
+                fflush(stderr);
+            }
+        } else {
+            if (blocks % BLOCK_COUNT_REPORT == 0) {
+                fprintf(stderr, "Number of processed patch blocks: %"PRIu64"\n", blocks);
+            }
         }
     }
 
+    if (block_count != NULL) {
+        fprintf(stderr, "\rUpgrade progress: 100.00%%\n");
+    }
+
     result = PF_OK;
 
 exit:
index 476e025779a7ecaddcfbca9554e2f68b93af8ffb..34ef2bd78808d44296fd15a7e81b02f16a10147a 100644 (file)
@@ -66,4 +66,5 @@ struct dec_funcs {
     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, size_t *free_space, bool write_all, struct dec_funcs *funcs);
+extern int apply_patch(const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size,
+    size_t *free_space, bool write_all, struct dec_funcs *funcs, uint64_t *block_count);
index d71d454ff187b864fd6f1d8827cfebde142dfbd8..f146b2e1955a58ba9af45b09096efd11d44f0512 100644 (file)
@@ -21,5 +21,5 @@
 int apply_patch_brotli(const char *source_file, const char *dest_file, TAR *patch_tar, size_t dest_size, size_t *free_space, bool write_all)
 {
     struct dec_funcs funcs = { init_brotli, decompress_brotli, free_brotli };
-    return apply_patch(source_file, dest_file, patch_tar, dest_size, free_space, write_all, &funcs);
+    return apply_patch(source_file, dest_file, patch_tar, dest_size, free_space, write_all, &funcs, NULL);
 }