Add support for multi-part delta 88/311688/9
authorMateusz Moscicki <m.moscicki2@partner.samsung.com>
Mon, 20 May 2024 13:52:38 +0000 (15:52 +0200)
committerMateusz Moscicki <m.moscicki2@partner.samsung.com>
Wed, 5 Jun 2024 10:47:55 +0000 (12:47 +0200)
Until now, update-manager expected the delta.tar(.gz) file in specified
directories.
This change adds the possibility to provide:

    delta-platform.tar(.gz) - to perform platform upgrade only

or

    delta-platform.tar(.gz) + delta-boot.tar(.gz) - to perform a complete upgrade

Change-Id: I0126823955bb548f7c92dae7e4aa5e823aba024e

update-manager/fota/fota-common.c
update-manager/fota/fota-installer.c
update-manager/fota/fota-manager.h
update-manager/fota/fota-storage-checker.c

index 25208b3cf9256caa001358784326c9afb6a15e99..ab0d5c32cb48646c6774bbfd3bc171a369249e02 100644 (file)
@@ -17,7 +17,7 @@
 #include "common/common.h"
 #include "fota-manager.h"
 
-int check_is_delta_appropriate(const char *delta_path, bool *checksum_available)
+int check_is_delta_appropriate(const char *delta_path, const char *delta_with_tools_path, bool *checksum_available)
 {
        int ret;
        bool do_checksum = false;
@@ -29,13 +29,13 @@ int check_is_delta_appropriate(const char *delta_path, bool *checksum_available)
                goto exit;
        }
 
-       ret = util_file_untar(delta_path, FOTA_DIR, DELTA_VERIFIER_BIN);
+       ret = util_file_untar(delta_with_tools_path, FOTA_DIR, DELTA_VERIFIER_BIN);
        if (ret < 0) {
                ret = -1;
                goto exit;
        }
 
-       ret = util_file_untar(delta_path, FOTA_DIR, FOTA_CHECKSUM_FILE);
+       ret = util_file_untar(delta_with_tools_path, FOTA_DIR, FOTA_CHECKSUM_FILE);
        if (ret < 0) {
                _FLOGW("There is no %s in delta", FOTA_CHECKSUM_FILE);
        } else {
index 82d0e61f9f3007efb23fd3fa0caaeb27150ffd54..73d88ba67ae59c73ec47237a16c89e69a9419b02 100644 (file)
@@ -1,5 +1,7 @@
 #include <assert.h>
+#include <alloca.h>
 #include <linux/limits.h>
+#include <stdlib.h>
 #include "../common/common.h"
 #include "fota-manager.h"
 
@@ -10,7 +12,7 @@
 #define FOTA_TRIGGER_PATH FOTA_DIR "/" FOTA_TRIGGER_FILE
 
 /* Empty string here is intentional */
-#define FOTA_TRIGGER_MODE_RO_UPDATE_AND_FINISH_UPDATE ""
+#define FOTA_TRIGGER_MODE_RO_UPDATE_AND_FINISH_UPDATE "--ro-update-and-finish"
 #define FOTA_TRIGGER_MODE_RO_UPDATE "--ro-update"
 #define FOTA_TRIGGER_MODE_FINISH_UPDATE "--finish"
 
@@ -35,48 +37,119 @@ static char *get_sender_appid(pid_t pid)
        return result;
 }
 
-static char *check_delta_exist(char *base_dir)
+bool check_delta_exist(const char *base_dir, struct deltas *deltas)
 {
+       assert(deltas);
        assert(base_dir);
-       char *client_delta_path = NULL;
 
-       const char *delta_files[] = {FOTA_DELTA_COMPRESSED_FILENAME,
-                                    FOTA_DELTA_FILENAME};
+       #define I_DELTA 0
+       #define I_DELTA_BOOT 1
+       #define I_DELTA_PLATFORM 2
 
+       const char *delta_files[] = {FOTA_DELTA_BASENAME,
+                                    FOTA_DELTA_BOOT_BASENAME,
+                                    FOTA_DELTA_PLATFORM_BASENAME};
+       const char *extensions[] = {".tar", ".tar.gz"};
+       bool delta_found = false;
+       char *delta_table[3] = {NULL, NULL, NULL};
+       deltas->first_delta = NULL;
+       deltas->second_delta = NULL;
+
+       // Collect info about existing delta files
        for (size_t n = 0; n < G_N_ELEMENTS(delta_files); n++) {
-               gchar *tmp_file_name = g_strconcat(base_dir, "/", delta_files[n], NULL);
-               if (tmp_file_name == NULL) {
-                       _FLOGE("Out of memory error");
-                       break;
+               for (size_t e = 0; e < G_N_ELEMENTS(extensions); e++) {
+                       gchar *tmp_file_name = g_strconcat(base_dir, "/", delta_files[n], extensions[e], NULL);
+                       if (tmp_file_name == NULL) {
+                               _FLOGE("Out of memory error");
+                               break;
+                       }
+
+                       _FLOGD("Checking: %s", tmp_file_name);
+                       if (g_file_test(tmp_file_name, G_FILE_TEST_EXISTS)) {
+                               // If there is a two version of the same delta
+                               // (tar and tar.gz), we treat it as an error
+                               if (delta_table[n]) {
+                                       _FLOGE("There are a tar and tar.gz version of delta file in %s", base_dir);
+                                       free(tmp_file_name);
+                                       goto error;
+                               }
+                               delta_table[n] = tmp_file_name;
+
+                               delta_found = true;
+                       } else {
+                               free(tmp_file_name);
+                       }
                }
+       }
 
-               _FLOGD("Checking: %s", tmp_file_name);
-               if (g_file_test(tmp_file_name, G_FILE_TEST_EXISTS)) {
-                       client_delta_path = tmp_file_name;
-                       break;
+       if (!delta_found)
+               goto error;
+
+       // Check if there is complete delta and delta-boot or deta-platform
+       if (delta_table[I_DELTA]) {
+               if (delta_table[I_DELTA_BOOT] || delta_table[I_DELTA_PLATFORM]) {
+                       _FLOGE("There are complete delta (delta.tar(.gz)) and partial delta (delta-boot or delta-platform) in %s", base_dir);
+                       goto error;
                }
-               free(tmp_file_name);
        }
 
-       return client_delta_path;
+       // Check if there is only delta-boot
+       if (delta_table[I_DELTA_BOOT] && delta_table[I_DELTA_PLATFORM] == NULL) {
+               _FLOGE("There is only delta-boot file, and this is not enough to perfrom the upgrade");
+               goto error;
+       }
+
+       if (delta_table[I_DELTA]) {
+               deltas->first_delta = delta_table[I_DELTA];
+               _FLOGD("Found complete delta: %s", deltas->first_delta);
+       }
+
+       if (delta_table[I_DELTA_PLATFORM]) {
+               deltas->first_delta = delta_table[I_DELTA_PLATFORM];
+               _FLOGD("Found platform delta: %s", deltas->first_delta);
+       }
+
+       if (delta_table[I_DELTA_BOOT]) {
+               deltas->second_delta = delta_table[I_DELTA_BOOT];
+               _FLOGD("Found boot delta: %s", deltas->second_delta);
+       }
+
+       return true;
+error:
+       for (size_t i = 0; i < G_N_ELEMENTS(delta_table); i++)
+               free(delta_table[i]);
+       return false;
+
+       #undef I_DELTA
+       #undef I_DELTA_BOOT
+       #undef I_DELTA_PLATFORM
 }
 
-static char *find_delta_dir(const char *appid)
+static char *find_delta_dir(const char *appid, struct deltas *deltas)
 {
-       char *client_delta_path = NULL;
+       char *client_delta_dir = NULL;
        if (appid != NULL) {
+               // Check if delta exists in app dir
                char *dir_path = g_strjoin("/", APP_SHARED_DIR, appid, APP_SHARED_DATA, NULL);
-               client_delta_path = check_delta_exist(dir_path);
-               free(dir_path);
+               if (check_delta_exist(dir_path, deltas)) {
+                       client_delta_dir = dir_path;
+               } else {
+                       free(dir_path);
+               }
        }
 
-       if (client_delta_path == NULL)
-               client_delta_path = check_delta_exist(FOTA_DIR);
+       // If not - check if delta exists in FOTA_DIR
+       if (client_delta_dir == NULL && check_delta_exist(FOTA_DIR, deltas)) {
+               client_delta_dir = strdup(FOTA_DIR);
+               if (client_delta_dir == NULL) {
+                       _FLOGE("Out of memory error");
+               }
+       }
 
-       return client_delta_path;
+       return client_delta_dir;
 }
 
-int find_delta_and_prepare_script(pid_t sender_pid, gchar **client_delta_path)
+int find_delta_and_prepare_script(pid_t sender_pid, struct deltas *deltas)
 {
        int ret = 0;
        char *appid = NULL;
@@ -86,17 +159,17 @@ int find_delta_and_prepare_script(pid_t sender_pid, gchar **client_delta_path)
        appid = get_sender_appid(sender_pid);
 
        /* Find the delta file path */
-       *client_delta_path = find_delta_dir(appid);
+       char *delta_dir = find_delta_dir(appid, deltas);
        if (appid != NULL) {
                free(appid);
                appid = NULL;
        }
 
-       if (*client_delta_path == NULL) {
+       if (delta_dir == NULL) {
                _FLOGE("Delta file not found");
                return -1;
        }
-       _FLOGI("Delta found: %s", *client_delta_path);
+       _FLOGI("Delta directory found: %s", delta_dir);
 
        /* Prepare fota dir */
        ret = util_file_mkdir(FOTA_DIR);
@@ -105,7 +178,10 @@ int find_delta_and_prepare_script(pid_t sender_pid, gchar **client_delta_path)
        }
 
        /* Check client have appropriate delta  */
-       if (check_is_delta_appropriate(*client_delta_path, &check_sha) != 0) {
+       if (deltas->second_delta && check_is_delta_appropriate(deltas->second_delta, deltas->first_delta, &check_sha) != 0) {
+               return -1;
+       }
+       if (check_is_delta_appropriate(deltas->first_delta, deltas->first_delta, &check_sha) != 0) {
                return -1;
        }
 
@@ -121,7 +197,7 @@ int find_delta_and_prepare_script(pid_t sender_pid, gchar **client_delta_path)
        }
 
        /* Trigger update */
-       ret = util_file_untar(*client_delta_path, FOTA_DIR, FOTA_TRIGGER_FILE);
+       ret = util_file_untar(deltas->first_delta, FOTA_DIR, FOTA_TRIGGER_FILE);
        if (ret < 0) {
                return -1;
        }
@@ -139,21 +215,39 @@ int find_delta_and_prepare_script(pid_t sender_pid, gchar **client_delta_path)
        return 0;
 }
 
+int execute_upgrade_trigger(struct deltas *deltas, char *mode)
+{
+       assert(mode);
+       #define MAX_ARGS 5
+
+       char **args = alloca(sizeof(char*)*MAX_ARGS);
+       size_t arg = 0;
+       args[arg++] = FOTA_TRIGGER_PATH;
+       if (deltas && deltas->first_delta)
+               args[arg++] = deltas->first_delta;
+       if (deltas && deltas->second_delta)
+               args[arg++] = deltas->second_delta;
+       args[arg++] = mode;
+       args[arg] = NULL;
+
+       return execv(FOTA_TRIGGER_PATH, args);
+       #undef MAX_ARGS
+}
+
 int fota_installer_ro_update_and_finish_update(pid_t sender_pid)
 {
        int ret = 0, status = 0, exec_status = 0;
-       gchar *client_delta_path = NULL;
+
        pid_t pid;
 
-       ret = find_delta_and_prepare_script(sender_pid, &client_delta_path);
+       struct deltas deltas;
+       ret = find_delta_and_prepare_script(sender_pid, &deltas);
+
        if (ret < 0) {
                status = ret;
                goto execute_destroy;
        }
 
-       /* This shouldn't fail. Added to make compiler happy */
-       assert(client_delta_path);
-
        /* All basic checks succeeded, following is what happens next: We
         * double-fork() the process and run actual upgrade script in the child
         * process. We do this so that we can at least inform API client that
@@ -176,11 +270,11 @@ int fota_installer_ro_update_and_finish_update(pid_t sender_pid)
                                exit(EXIT_SUCCESS);
                }
 
-               ret = execl(FOTA_TRIGGER_PATH, FOTA_TRIGGER_PATH, client_delta_path,
-                       FOTA_TRIGGER_MODE_RO_UPDATE_AND_FINISH_UPDATE, NULL);
+               ret = execute_upgrade_trigger(&deltas,
+                                             FOTA_TRIGGER_MODE_RO_UPDATE_AND_FINISH_UPDATE);
 
                if (ret == -1)
-                       _FLOGE("Failed to execl(%s %s %s)", FOTA_TRIGGER_PATH, client_delta_path,
+                       _FLOGE("Failed to execl(%s %s %s %s)", FOTA_TRIGGER_PATH, deltas.first_delta, deltas.second_delta,
                                FOTA_TRIGGER_MODE_RO_UPDATE_AND_FINISH_UPDATE);
 
                exit(EXIT_FAILURE);
@@ -196,8 +290,10 @@ int fota_installer_ro_update_and_finish_update(pid_t sender_pid)
        }
 
 execute_destroy:
-       if (client_delta_path)
-               free(client_delta_path);
+       if (deltas.first_delta)
+               free(deltas.first_delta);
+       if (deltas.second_delta)
+               free(deltas.second_delta);
 
        return status;
 }
@@ -205,18 +301,16 @@ execute_destroy:
 int fota_installer_ro_update(pid_t sender_pid)
 {
        int ret = 0, status = 0, exec_status = 0;
-       gchar *client_delta_path = NULL;
        pid_t pid;
 
-       ret = find_delta_and_prepare_script(sender_pid, &client_delta_path);
+       struct deltas deltas;
+       ret = find_delta_and_prepare_script(sender_pid, &deltas);
+
        if (ret < 0) {
                status = ret;
                goto execute_destroy;
        }
 
-       /* This shouldn't fail. Added to make compiler happy */
-       assert(client_delta_path);
-
        /*
        * This is supposed to bo called asynchronously, so we do
        * care if and when the script finishes.
@@ -227,12 +321,13 @@ int fota_installer_ro_update(pid_t sender_pid)
                status = -1;
                goto execute_destroy;
        } else if (pid == 0) {
-               ret = execl(FOTA_TRIGGER_PATH, FOTA_TRIGGER_PATH, client_delta_path,
-                       FOTA_TRIGGER_MODE_RO_UPDATE, NULL);
+
+               ret = execute_upgrade_trigger(&deltas,
+                                             FOTA_TRIGGER_MODE_RO_UPDATE);
 
                if (ret == -1)
-                       _FLOGE("Failed to execl(%s %s %s)", FOTA_TRIGGER_PATH, client_delta_path,
-                               FOTA_TRIGGER_MODE_RO_UPDATE);
+                       _FLOGE("Failed to execl(%s %s %s %s)", FOTA_TRIGGER_PATH, deltas.first_delta,
+                               deltas.second_delta, FOTA_TRIGGER_MODE_RO_UPDATE);
 
                exit(EXIT_FAILURE);
        }
@@ -248,8 +343,10 @@ int fota_installer_ro_update(pid_t sender_pid)
        status = exec_status > 0 ? -exec_status : exec_status;
 
 execute_destroy:
-       if (client_delta_path)
-               free(client_delta_path);
+       if (deltas.first_delta)
+               free(deltas.first_delta);
+       if (deltas.second_delta)
+               free(deltas.second_delta);
 
        return status;
 }
@@ -274,7 +371,7 @@ int fota_installer_finish_update(void)
                _FLOGE("Failed to fork");
                return -1;
        } else if (pid == 0) {
-               ret = execl(FOTA_TRIGGER_PATH, FOTA_TRIGGER_PATH, FOTA_TRIGGER_MODE_FINISH_UPDATE, NULL);
+               ret = execute_upgrade_trigger(NULL, FOTA_TRIGGER_MODE_FINISH_UPDATE);
 
                if (ret == -1)
                        _FLOGE("Failed to execl(%s %s)", FOTA_TRIGGER_PATH, FOTA_TRIGGER_MODE_FINISH_UPDATE);
@@ -290,4 +387,4 @@ int fota_installer_finish_update(void)
        exec_status = WEXITSTATUS(exec_status);
 
        return exec_status > 0 ? -exec_status : exec_status;
-}
\ No newline at end of file
+}
index af36888a11ba43812d96aa1f911e5e057e67e6b8..24ef468a32f21c9d0093cec468d612280efc08e7 100644 (file)
@@ -5,6 +5,7 @@
 #include <dlog.h>
 #include <system_info.h>
 #include <unistd.h>
+#include <stdbool.h>
 
 /* Log */
 #define FOTA_LOG_TAG "FOTA_MANAGER"
@@ -15,8 +16,9 @@
 #define _FLOGE(fmt, arg...) SLOG(LOG_ERROR, FOTA_LOG_TAG, fmt, ##arg)
 
 /* Constant */
-#define FOTA_DELTA_FILENAME "delta.tar"
-#define FOTA_DELTA_COMPRESSED_FILENAME "delta.tar.gz"
+#define FOTA_DELTA_BASENAME "delta"
+#define FOTA_DELTA_BOOT_BASENAME "delta-boot"
+#define FOTA_DELTA_PLATFORM_BASENAME "delta-platform"
 
 #define FOTA_DIR "/opt/usr/data/fota"
 #define FOTA_STATUS_DIR "/opt/data/update"
@@ -40,6 +42,11 @@ typedef enum {
        FOTA_EVENT_REBOOT = 2
 } fota_event_idx;
 
+struct deltas {
+       char *first_delta;
+       char *second_delta;
+};
+
 /* Function */
 int fota_client_controller_process_event(void);
 int fota_client_controller_add_event(fota_event_idx, const char *);
@@ -54,6 +61,7 @@ int fota_installer_finish_update(void);
 
 void fota_storage_checker_plug(int, const char *);
 void fota_storage_checker_unplug(int, const char *);
-int check_is_delta_appropriate(const char *delta_path, bool *checksum_available);
+int check_is_delta_appropriate(const char *delta_path, const char *delta_with_tools_path, bool *checksum_available);
+bool check_delta_exist(const char *base_dir, struct deltas *deltas);
 
 #endif /* __FOTA_MANAGER_H__ */
index bc06cdc3a9290f6ffbb3b8801ea683090ca8ad7d..d60b31d8c81d50eeee1a1ec218765a1448394e8d 100644 (file)
@@ -4,27 +4,21 @@
 static int fota_storage_id = -1;
 
 
-gchar *fota_storage_find_delta(const char* mount_path, const char *folder_name)
+int fota_storage_find_delta(const char* mount_path, const char *folder_name, struct deltas *deltas)
 {
        int ret = 0;
        bool is_appropriate = false;
-       gchar *storage_delta_path = NULL;
+       gchar *storage_delta_dir = NULL;
 
-       /* Check storage have delta.tar */
-       storage_delta_path = g_strjoin("/", mount_path, folder_name, FOTA_DELTA_FILENAME, NULL);
-       if (storage_delta_path == NULL) {
-               _FLOGI("Memory allocation error for storage delta path.");
+       storage_delta_dir = g_strjoin("/", mount_path, folder_name, NULL);
+       if (storage_delta_dir == NULL) {
+               _FLOGE("Out of memory");
                goto verify_destroy;
        }
-       if (!g_file_test(storage_delta_path, G_FILE_TEST_EXISTS)) {
-               gchar *tmp_storage_delta_path = storage_delta_path;
-               storage_delta_path = g_strconcat(tmp_storage_delta_path, ".gz", NULL);
-               free(tmp_storage_delta_path);
-
-               if (!g_file_test(storage_delta_path, G_FILE_TEST_EXISTS)) {
-                       _FLOGI("Storage doesn't have %s", storage_delta_path);
-                       goto verify_destroy;
-               }
+
+       if (!check_delta_exist(storage_delta_dir, deltas)) {
+               _FLOGI("Storage doesn't have %s", storage_delta_dir);
+               goto verify_destroy;
        }
 
        /* Prepare fota dir */
@@ -34,28 +28,33 @@ gchar *fota_storage_find_delta(const char* mount_path, const char *folder_name)
        }
 
        /* Check storage have appropriate delta */
-       if (check_is_delta_appropriate(storage_delta_path, NULL) != 0)
+       if (deltas->second_delta && check_is_delta_appropriate(deltas->second_delta, deltas->first_delta, NULL) != 0)
                goto verify_destroy;
 
-       _FLOGI("Success to find delta from storage(%s)", storage_delta_path);
+       if (check_is_delta_appropriate(deltas->first_delta, deltas->first_delta, NULL) != 0)
+               goto verify_destroy;
+
+       _FLOGI("Success to find delta from storage(%s)", storage_delta_dir);
        is_appropriate = true;
 
 verify_destroy:
-       if (!is_appropriate && storage_delta_path) {
-               free(storage_delta_path);
-               storage_delta_path = NULL;
+       if (!is_appropriate) {
+               if (storage_delta_dir) {
+                       free(storage_delta_dir);
+               }
+               ret = -1;
        }
 
-       return storage_delta_path;
+       return ret;
 }
 
-gchar *fota_storage_search_delta_path(const char *mount_path)
+int fota_storage_search_delta_path(const char *mount_path, struct deltas *deltas)
 {
        GFile *mount = NULL;
        GFileEnumerator *enumerator = NULL;
        GFileInfo *info = NULL;
        GError* error = NULL;
-       gchar* delta_path = NULL;
+       int result = -1;
 
        mount = g_file_new_for_path(mount_path);
        enumerator = g_file_enumerate_children(mount, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error);
@@ -67,8 +66,10 @@ gchar *fota_storage_search_delta_path(const char *mount_path)
 
        while ((info = g_file_enumerator_next_file(enumerator, NULL, NULL)) != NULL) {
                if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) {
-                       delta_path = fota_storage_find_delta(mount_path, g_file_info_get_name(info));
-                       if (delta_path != NULL)
+                       result = fota_storage_find_delta(mount_path,
+                                                        g_file_info_get_name(info),
+                                                        deltas);
+                       if (result >= 0)
                                break;
                }
 
@@ -84,7 +85,7 @@ search_destroy:
 
        g_object_unref(mount);
 
-       return delta_path;
+       return result;
 }
 
 void fota_storage_checker_plug(int storage_id, const char *mount_path)
@@ -93,12 +94,26 @@ void fota_storage_checker_plug(int storage_id, const char *mount_path)
        gchar *delta_path = NULL;
 
        _FLOGI("Storage mounted with %s, start process to check local delta", mount_path);
-       delta_path = fota_storage_search_delta_path(mount_path);
-       if (delta_path == NULL) {
+
+       struct deltas deltas;
+       if (fota_storage_search_delta_path(mount_path, &deltas) < 0) {
                _FLOGI("Failed to search delta path from %s", mount_path);
                goto plug_destroy;
        }
 
+       delta_path = g_strjoin(" ",
+                              deltas.first_delta,
+                              deltas.second_delta,
+                              NULL);
+       free(deltas.first_delta);
+       free(deltas.second_delta);
+
+       if (delta_path == NULL) {
+               _FLOGE("Out of memory");
+               goto plug_destroy;
+       }
+
+
        ret = fota_client_controller_add_event(FOTA_EVENT_PLUG, delta_path);
        if (ret < 0) {
                _FLOGE("Failed to add fota client event : %d, idx : %d, value : %s",