From: Mateusz Moscicki Date: Mon, 20 May 2024 13:52:38 +0000 (+0200) Subject: Add support for multi-part delta X-Git-Tag: accepted/tizen/unified/20240614.085117~3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F88%2F311688%2F9;p=platform%2Fcore%2Fsystem%2Fupdate-control.git Add support for multi-part delta 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 --- diff --git a/update-manager/fota/fota-common.c b/update-manager/fota/fota-common.c index 25208b3..ab0d5c3 100644 --- a/update-manager/fota/fota-common.c +++ b/update-manager/fota/fota-common.c @@ -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 { diff --git a/update-manager/fota/fota-installer.c b/update-manager/fota/fota-installer.c index 82d0e61..73d88ba 100644 --- a/update-manager/fota/fota-installer.c +++ b/update-manager/fota/fota-installer.c @@ -1,5 +1,7 @@ #include +#include #include +#include #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 +} diff --git a/update-manager/fota/fota-manager.h b/update-manager/fota/fota-manager.h index af36888..24ef468 100644 --- a/update-manager/fota/fota-manager.h +++ b/update-manager/fota/fota-manager.h @@ -5,6 +5,7 @@ #include #include #include +#include /* 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__ */ diff --git a/update-manager/fota/fota-storage-checker.c b/update-manager/fota/fota-storage-checker.c index bc06cdc..d60b31d 100644 --- a/update-manager/fota/fota-storage-checker.c +++ b/update-manager/fota/fota-storage-checker.c @@ -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",