Add support for multi-part delta 88/311688/1
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>
Thu, 23 May 2024 14:12:34 +0000 (16:12 +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-installer.c
update-manager/fota/fota-manager.h
update-manager/fota/fota-storage-checker.c

index 82d0e61f9f3007efb23fd3fa0caaeb27150ffd54..97fc5d08d6c044a94422ec7ca23affbd3311065b 100644 (file)
@@ -1,5 +1,6 @@
 #include <assert.h>
 #include <linux/limits.h>
+#include <stdlib.h>
 #include "../common/common.h"
 #include "fota-manager.h"
 
@@ -35,48 +36,133 @@ static char *get_sender_appid(pid_t pid)
        return result;
 }
 
-static char *check_delta_exist(char *base_dir)
+bool check_delta_exist(char *base_dir)
 {
        assert(base_dir);
-       char *client_delta_path = NULL;
 
-       const char *delta_files[] = {FOTA_DELTA_COMPRESSED_FILENAME,
-                                    FOTA_DELTA_FILENAME};
+       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;
+       int check_table[3] = {0, 0, 0};
 
        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)) {
+                               check_table[n]++;
+                               delta_found = true;
+                       }
+                       free(tmp_file_name);
                }
+       }
+
+       if (!delta_found)
+               return false;
 
-               _FLOGD("Checking: %s", tmp_file_name);
-               if (g_file_test(tmp_file_name, G_FILE_TEST_EXISTS)) {
-                       client_delta_path = tmp_file_name;
-                       break;
+       // Check if there is only one version of delta file - tar or tar.gz
+       for (size_t i = 0; i < 3; i++) {
+               if (check_table[i] > 1) {
+                       _FLOGE("There are a tar and tar.gz version of delta file in %s", base_dir);
+                       return false;
                }
-               free(tmp_file_name);
        }
 
-       return client_delta_path;
+       // Check if there is complete delta and delta-boot or deta-platform
+       if (check_table[0] > 0) {
+               if (check_table[1] > 0 || check_table[2] > 0) {
+                       _FLOGE("There are complete delta (delta.tar(.gz)) and partial delta (delta-boot or delta-platform) in %s", base_dir);
+                       return false;
+               }
+       }
+
+       // Check if there is only delta-boot
+       if (check_table[1] > 0 && check_table[2] == 0) {
+               _FLOGE("There is only delta-boot file, and this is not enough to perfrom the upgrade");
+               return false;
+       }
+
+       return delta_found;
 }
 
 static char *find_delta_dir(const char *appid)
 {
-       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)) {
+                       client_delta_dir = dir_path;
+               } else {
+                       free(dir_path);
+               }
        }
 
-       if (client_delta_path == NULL)
-               client_delta_path = check_delta_exist(FOTA_DIR);
+       if (client_delta_dir == NULL && check_delta_exist(FOTA_DIR)) {
+               // If not - check if delta exists in FOTA_DIR
+               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)
+static char *check_delta_file_exist(char *delta_dir, const char *delta_basename)
+{
+       assert(delta_dir);
+       assert(delta_basename);
+
+       const char *extensions[] = {".tar", ".tar.gz"};
+       for (size_t e = 0; e < G_N_ELEMENTS(extensions); e++) {
+               char *path = g_strconcat(delta_dir, "/", delta_basename, extensions[e], NULL);
+               if (g_file_test(path, G_FILE_TEST_EXISTS)) {
+                       return path;
+               }
+               free(path);
+       }
+       return NULL;
+}
+
+int get_delta_file_names(char *delta_dir, struct deltas *deltas) {
+       assert(delta_dir);
+       assert(deltas);
+
+       // Check if there are a boot and platform delta
+       // We don't need to check if the set of deltas is correnct, because it
+       // has been checked in check_delta_exist()
+       const char *delta_files[] = {FOTA_DELTA_BASENAME,
+                                    FOTA_DELTA_BOOT_BASENAME,
+                                    FOTA_DELTA_PLATFORM_BASENAME};
+
+       size_t count = 0;
+       char **tmp_array[] = {&deltas->first_delta, &deltas->second_delta};
+
+       for (size_t n = 0; n < G_N_ELEMENTS(tmp_array); n++) {
+               *tmp_array[n] = NULL;
+       }
+
+       for (size_t n = 0; n < G_N_ELEMENTS(delta_files); n++) {
+               char *path = check_delta_file_exist(delta_dir, delta_files[n]);;
+               if (path == NULL) {
+                       continue;
+               }
+               _FLOGD("Found delta: %s", path);
+               *tmp_array[count] = path;
+               count++;
+       }
+       return count;
+}
+
+int find_delta_and_prepare_script(pid_t sender_pid, struct deltas *deltas)
 {
        int ret = 0;
        char *appid = NULL;
@@ -86,17 +172,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);
        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);
@@ -104,8 +190,22 @@ int find_delta_and_prepare_script(pid_t sender_pid, gchar **client_delta_path)
                return -1;
        }
 
+       if (get_delta_file_names(delta_dir, deltas) <= 0) {
+               return -1;
+       }
+
        /* Check client have appropriate delta  */
-       if (check_is_delta_appropriate(*client_delta_path, &check_sha) != 0) {
+
+       // We take scripts from the first delta, so only for it we check
+       // whether the checksum is available
+       if (check_is_delta_appropriate(deltas->first_delta,
+                                      &check_sha) != 0) {
+               return -1;
+       }
+
+       if (deltas->second_delta &&
+                 check_is_delta_appropriate(deltas->second_delta,
+                                            NULL) != 0) {
                return -1;
        }
 
@@ -121,7 +221,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 +239,43 @@ 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(deltas);
+       assert(mode);
+
+       int ret;
+       if (deltas->second_delta == NULL) {
+               ret = execl(FOTA_TRIGGER_PATH,
+                           FOTA_TRIGGER_PATH,
+                           deltas->first_delta,
+                           mode,
+                           NULL);
+       } else {
+               ret = execl(FOTA_TRIGGER_PATH,
+                           FOTA_TRIGGER_PATH,
+                           deltas->first_delta,
+                           deltas->second_delta,
+                           mode,
+                           NULL);
+       }
+       return ret;
+}
+
 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 +298,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 +318,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 +329,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 +349,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 +371,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;
 }
@@ -290,4 +415,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..ebd96a817f644fb8d018bbb0172292f3b73c1a57 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 *);
@@ -55,5 +62,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);
+bool check_delta_exist(char *base_dir);
+int get_delta_file_names(char *delta_dir, struct deltas *deltas);
 
 #endif /* __FOTA_MANAGER_H__ */
index bc06cdc3a9290f6ffbb3b8801ea683090ca8ad7d..945359c1a13710cdaa269f1bdce5308296090435 100644 (file)
@@ -4,27 +4,26 @@
 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)) {
+               _FLOGI("Storage doesn't have %s", storage_delta_dir);
+               goto verify_destroy;
+       }
+
+       if (get_delta_file_names(storage_delta_dir, deltas) <= 0) {
+               _FLOGE("There are no delta files in %s", storage_delta_dir);
+               goto verify_destroy;
        }
 
        /* Prepare fota dir */
@@ -34,28 +33,34 @@ 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 (check_is_delta_appropriate(deltas->first_delta, NULL) != 0)
+               goto verify_destroy;
+
+       if (deltas->second_delta &&
+           check_is_delta_appropriate(deltas->second_delta, NULL) != 0)
                goto verify_destroy;
 
-       _FLOGI("Success to find delta from storage(%s)", storage_delta_path);
+       _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 +72,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 +91,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 +100,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",