From b95cd2df143d20a5dc5a236e83b455a4c7d2d9b1 Mon Sep 17 00:00:00 2001 From: hedger Date: Mon, 4 Jul 2022 16:36:12 +0300 Subject: [PATCH] [FL-2578] Updater fixes related to /int handling (#1359) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updater fixes related to /int handling updater: performing factory reset on update, checking for LFS free space before updating, fixed improper error handling on backup/restore operations, rebalanced update stage weights for better progress visuals scripts: added CLI output validation for selfupdate.py storage: added pointer validation in storage_int_common_fs_info desktop: fixed crash on rendering invalid slideshows * Typo fix * rpc: Updated protobuf to 0.9 * rpc: removed updater status conversion Co-authored-by: あく --- applications/desktop/helpers/slideshow.c | 12 +++++-- applications/desktop/helpers/slideshow.h | 1 + .../desktop/views/desktop_view_slideshow.c | 4 ++- applications/rpc/rpc_system.c | 4 --- applications/storage/storages/storage_int.c | 8 +++-- applications/updater/util/update_task.c | 15 ++++---- applications/updater/util/update_task_i.h | 2 ++ .../updater/util/update_task_worker_backup.c | 35 ++++++++++--------- .../updater/util/update_task_worker_flasher.c | 2 ++ assets/protobuf | 2 +- lib/update_util/update_operation.c | 12 ++++++- lib/update_util/update_operation.h | 2 ++ scripts/selfupdate.py | 15 ++++++-- 13 files changed, 76 insertions(+), 38 deletions(-) diff --git a/applications/desktop/helpers/slideshow.c b/applications/desktop/helpers/slideshow.c index 37f66cf35..593b2854a 100644 --- a/applications/desktop/helpers/slideshow.c +++ b/applications/desktop/helpers/slideshow.c @@ -12,6 +12,7 @@ struct Slideshow { Icon icon; uint32_t current_frame; + bool loaded; }; #pragma pack(push, 1) @@ -34,6 +35,7 @@ _Static_assert(sizeof(SlideshowFrameHeader) == 2, "Incorrect SlideshowFrameHeade Slideshow* slideshow_alloc() { Slideshow* ret = malloc(sizeof(Slideshow)); + ret->loaded = false; return ret; } @@ -52,7 +54,7 @@ void slideshow_free(Slideshow* slideshow) { bool slideshow_load(Slideshow* slideshow, const char* fspath) { Storage* storage = furi_record_open("storage"); File* slideshow_file = storage_file_alloc(storage); - bool load_success = false; + slideshow->loaded = false; do { if(!storage_file_open(slideshow_file, fspath, FSAM_READ, FSOM_OPEN_EXISTING)) { break; @@ -80,12 +82,16 @@ bool slideshow_load(Slideshow* slideshow, const char* fspath) { frame_header.size) { break; } - load_success = (frame_idx + 1) == header.frame_count; + slideshow->loaded = (frame_idx + 1) == header.frame_count; } } while(false); storage_file_free(slideshow_file); furi_record_close("storage"); - return load_success; + return slideshow->loaded; +} + +bool slideshow_is_loaded(Slideshow* slideshow) { + return slideshow->loaded; } bool slideshow_advance(Slideshow* slideshow) { diff --git a/applications/desktop/helpers/slideshow.h b/applications/desktop/helpers/slideshow.h index db19779a3..eeaac0e8b 100644 --- a/applications/desktop/helpers/slideshow.h +++ b/applications/desktop/helpers/slideshow.h @@ -8,6 +8,7 @@ Slideshow* slideshow_alloc(); void slideshow_free(Slideshow* slideshow); bool slideshow_load(Slideshow* slideshow, const char* fspath); +bool slideshow_is_loaded(Slideshow* slideshow); void slideshow_goback(Slideshow* slideshow); bool slideshow_advance(Slideshow* slideshow); void slideshow_draw(Slideshow* slideshow, Canvas* canvas, uint8_t x, uint8_t y); diff --git a/applications/desktop/views/desktop_view_slideshow.c b/applications/desktop/views/desktop_view_slideshow.c index 97f779b58..cd22b39de 100644 --- a/applications/desktop/views/desktop_view_slideshow.c +++ b/applications/desktop/views/desktop_view_slideshow.c @@ -21,7 +21,9 @@ static void desktop_view_slideshow_draw(Canvas* canvas, void* model) { DesktopSlideshowViewModel* m = model; canvas_clear(canvas); - slideshow_draw(m->slideshow, canvas, 0, 0); + if(slideshow_is_loaded(m->slideshow)) { + slideshow_draw(m->slideshow, canvas, 0, 0); + } } static bool desktop_view_slideshow_input(InputEvent* event, void* context) { diff --git a/applications/rpc/rpc_system.c b/applications/rpc/rpc_system.c index 45ae2e5a0..350602fd8 100644 --- a/applications/rpc/rpc_system.c +++ b/applications/rpc/rpc_system.c @@ -277,10 +277,6 @@ static void rpc_system_system_update_request_process(const PB_Main* request, voi UpdatePrepareResult update_prepare_result = update_operation_prepare(request->content.system_update_request.update_manifest); - /* RPC enum does not have such entry; setting to closest one */ - if(update_prepare_result == UpdatePrepareResultOutdatedManifestVersion) { - update_prepare_result = UpdatePrepareResultManifestInvalid; - } PB_Main* response = malloc(sizeof(PB_Main)); response->command_id = request->command_id; diff --git a/applications/storage/storages/storage_int.c b/applications/storage/storages/storage_int.c index 291fb426b..9b1ec700f 100644 --- a/applications/storage/storages/storage_int.c +++ b/applications/storage/storages/storage_int.c @@ -655,11 +655,13 @@ static FS_Error storage_int_common_fs_info( lfs_t* lfs = lfs_get_from_storage(storage); LFSData* lfs_data = lfs_data_get_from_storage(storage); - *total_space = lfs_data->config.block_size * lfs_data->config.block_count; + if(total_space) { + *total_space = lfs_data->config.block_size * lfs_data->config.block_count; + } lfs_ssize_t result = lfs_fs_size(lfs); - if(result >= 0) { - *free_space = *total_space - (result * lfs_data->config.block_size); + if(free_space && (result >= 0)) { + *free_space = (lfs_data->config.block_count - result) * lfs_data->config.block_size; } return storage_int_parse_error(result); diff --git a/applications/updater/util/update_task.c b/applications/updater/util/update_task.c index 316fe6802..47bb7ae5a 100644 --- a/applications/updater/util/update_task.c +++ b/applications/updater/util/update_task.c @@ -44,17 +44,17 @@ static const UpdateTaskStageGroupMap update_task_stage_progress[] = { [UpdateTaskStageReadManifest] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 5), [UpdateTaskStageLfsBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 15), - [UpdateTaskStageRadioImageValidate] = STAGE_DEF(UpdateTaskStageGroupRadio, 10), - [UpdateTaskStageRadioErase] = STAGE_DEF(UpdateTaskStageGroupRadio, 50), - [UpdateTaskStageRadioWrite] = STAGE_DEF(UpdateTaskStageGroupRadio, 90), - [UpdateTaskStageRadioInstall] = STAGE_DEF(UpdateTaskStageGroupRadio, 15), - [UpdateTaskStageRadioBusy] = STAGE_DEF(UpdateTaskStageGroupRadio, 60), + [UpdateTaskStageRadioImageValidate] = STAGE_DEF(UpdateTaskStageGroupRadio, 15), + [UpdateTaskStageRadioErase] = STAGE_DEF(UpdateTaskStageGroupRadio, 60), + [UpdateTaskStageRadioWrite] = STAGE_DEF(UpdateTaskStageGroupRadio, 80), + [UpdateTaskStageRadioInstall] = STAGE_DEF(UpdateTaskStageGroupRadio, 60), + [UpdateTaskStageRadioBusy] = STAGE_DEF(UpdateTaskStageGroupRadio, 80), [UpdateTaskStageOBValidation] = STAGE_DEF(UpdateTaskStageGroupOptionBytes, 10), - [UpdateTaskStageValidateDFUImage] = STAGE_DEF(UpdateTaskStageGroupFirmware, 100), + [UpdateTaskStageValidateDFUImage] = STAGE_DEF(UpdateTaskStageGroupFirmware, 50), [UpdateTaskStageFlashWrite] = STAGE_DEF(UpdateTaskStageGroupFirmware, 200), - [UpdateTaskStageFlashValidate] = STAGE_DEF(UpdateTaskStageGroupFirmware, 50), + [UpdateTaskStageFlashValidate] = STAGE_DEF(UpdateTaskStageGroupFirmware, 30), [UpdateTaskStageLfsRestore] = STAGE_DEF(UpdateTaskStageGroupPostUpdate, 30), @@ -214,6 +214,7 @@ UpdateTask* update_task_alloc() { update_task->storage = furi_record_open("storage"); update_task->file = storage_file_alloc(update_task->storage); update_task->status_change_cb = NULL; + update_task->boot_mode = furi_hal_rtc_get_boot_mode(); string_init(update_task->update_path); FuriThread* thread = update_task->thread = furi_thread_alloc(); diff --git a/applications/updater/util/update_task_i.h b/applications/updater/util/update_task_i.h index fb6b4ac11..95c63f644 100644 --- a/applications/updater/util/update_task_i.h +++ b/applications/updater/util/update_task_i.h @@ -1,6 +1,7 @@ #pragma once #include +#include #define UPDATE_TASK_NOERR 0 #define UPDATE_TASK_FAILED -1 @@ -14,6 +15,7 @@ typedef struct UpdateTask { File* file; updateProgressCb status_change_cb; void* status_change_cb_state; + FuriHalRtcBootMode boot_mode; } UpdateTask; void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress); diff --git a/applications/updater/util/update_task_worker_backup.c b/applications/updater/util/update_task_worker_backup.c index f1ce52817..1e8c2f09c 100644 --- a/applications/updater/util/update_task_worker_backup.c +++ b/applications/updater/util/update_task_worker_backup.c @@ -67,7 +67,6 @@ static bool update_task_post_update(UpdateTask* update_task) { string_get_cstr(update_task->update_path), LFS_BACKUP_DEFAULT_FILENAME, file_path); update_task_set_progress(update_task, UpdateTaskStageLfsRestore, 0); - update_operation_disarm(); CHECK_RESULT(lfs_backup_unpack(update_task->storage, string_get_cstr(file_path))); @@ -117,28 +116,32 @@ static bool update_task_post_update(UpdateTask* update_task) { int32_t update_task_worker_backup_restore(void* context) { furi_assert(context); UpdateTask* update_task = context; - bool success = false; - FuriHalRtcBootMode boot_mode = furi_hal_rtc_get_boot_mode(); + FuriHalRtcBootMode boot_mode = update_task->boot_mode; if((boot_mode != FuriHalRtcBootModePreUpdate) && (boot_mode != FuriHalRtcBootModePostUpdate)) { - /* no idea how we got here. Clear to normal boot */ - update_operation_disarm(); + /* no idea how we got here. Do nothing */ return UPDATE_TASK_NOERR; } - if(!update_task_parse_manifest(update_task)) { - return UPDATE_TASK_FAILED; - } + bool success = false; + do { + if(!update_task_parse_manifest(update_task)) { + break; + } - /* Waiting for BT service to 'start', so we don't race for boot mode flag */ - furi_record_open("bt"); - furi_record_close("bt"); + /* Waiting for BT service to 'start', so we don't race for boot mode flag */ + furi_record_open("bt"); + furi_record_close("bt"); - if(boot_mode == FuriHalRtcBootModePreUpdate) { - success = update_task_pre_update(update_task); - } else if(boot_mode == FuriHalRtcBootModePostUpdate) { - success = update_task_post_update(update_task); - } + if(boot_mode == FuriHalRtcBootModePreUpdate) { + success = update_task_pre_update(update_task); + } else if(boot_mode == FuriHalRtcBootModePostUpdate) { + success = update_task_post_update(update_task); + if(success) { + update_operation_disarm(); + } + } + } while(false); if(!success) { update_task_set_progress(update_task, UpdateTaskStageError, 0); diff --git a/applications/updater/util/update_task_worker_flasher.c b/applications/updater/util/update_task_worker_flasher.c index 1f22e2f72..d02858bc6 100644 --- a/applications/updater/util/update_task_worker_flasher.c +++ b/applications/updater/util/update_task_worker_flasher.c @@ -340,6 +340,8 @@ int32_t update_task_worker_flash_writer(void* context) { } furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate); + // Format LFS before restoring backup on next boot + furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); success = true; diff --git a/assets/protobuf b/assets/protobuf index e3d9cdb66..6c1b8ae66 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit e3d9cdb66ce789f84f6f8e0bdd6d022187964425 +Subproject commit 6c1b8ae66a85bcd7e79e993a0b5573c38c302db5 diff --git a/lib/update_util/update_operation.c b/lib/update_util/update_operation.c index 142dcaaaa..9082d2625 100644 --- a/lib/update_util/update_operation.c +++ b/lib/update_util/update_operation.c @@ -12,6 +12,8 @@ #define UPDATE_ROOT_DIR "/ext" UPDATE_DIR_DEFAULT_REL_PATH #define UPDATE_PREFIX "/ext" UPDATE_DIR_DEFAULT_REL_PATH "/" #define UPDATE_SUFFIX "/" UPDATE_MANIFEST_DEFAULT_NAME +/* Need at least 4 free LFS pages before update */ +#define UPDATE_MIN_INT_FREE_SPACE 4 * 4 * 1024 static const char* update_prepare_result_descr[] = { [UpdatePrepareResultOK] = "OK", @@ -22,6 +24,7 @@ static const char* update_prepare_result_descr[] = { [UpdatePrepareResultStageIntegrityError] = "Corrupted Stage2 loader", [UpdatePrepareResultManifestPointerError] = "Failed to create update pointer file", [UpdatePrepareResultOutdatedManifestVersion] = "Update package is too old", + [UpdatePrepareResultIntFull] = "Need more free space in internal storage", }; const char* update_operation_describe_preparation_result(const UpdatePrepareResult value) { @@ -133,15 +136,22 @@ static bool update_operation_persist_manifest_path(Storage* storage, const char* } UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { - UpdatePrepareResult result = UpdatePrepareResultManifestFolderNotFound; + UpdatePrepareResult result = UpdatePrepareResultIntFull; Storage* storage = furi_record_open("storage"); UpdateManifest* manifest = update_manifest_alloc(); File* file = storage_file_alloc(storage); + uint64_t free_int_space; string_t stage_path; string_init(stage_path); do { + if((storage_common_fs_info(storage, "/int", NULL, &free_int_space) != FSE_OK) || + (free_int_space < UPDATE_MIN_INT_FREE_SPACE)) { + break; + } + if(storage_common_stat(storage, manifest_file_path, NULL) != FSE_OK) { + result = UpdatePrepareResultManifestFolderNotFound; break; } diff --git a/lib/update_util/update_operation.h b/lib/update_util/update_operation.h index 39d580283..056520e11 100644 --- a/lib/update_util/update_operation.h +++ b/lib/update_util/update_operation.h @@ -32,6 +32,8 @@ typedef enum { UpdatePrepareResultManifestPointerError, UpdatePrepareResultTargetMismatch, UpdatePrepareResultOutdatedManifestVersion, + UpdatePrepareResultIntFull, + UpdatePrepareResultUnspecifiedError, } UpdatePrepareResult; const char* update_operation_describe_preparation_result(const UpdatePrepareResult value); diff --git a/scripts/selfupdate.py b/scripts/selfupdate.py index a22ca1f59..6d7057474 100644 --- a/scripts/selfupdate.py +++ b/scripts/selfupdate.py @@ -76,12 +76,15 @@ class Main(App): manifest_name, pkg_name = manifest_path.parts[-1], manifest_path.parts[-2] pkg_dir_name = self.args.pkg_dir_name or pkg_name - flipper_update_path = f"/ext/update/{pkg_dir_name}" + update_root = "/ext/update" + flipper_update_path = f"{update_root}/{pkg_dir_name}" self.logger.info(f'Installing "{pkg_name}" from {flipper_update_path}') # if not os.path.exists(self.args.manifest_path): # self.logger.error("Error: package not found") - if not self.mkdir_on_storage(storage, flipper_update_path): + if not self.mkdir_on_storage( + storage, update_root + ) or not self.mkdir_on_storage(storage, flipper_update_path): self.logger.error(f"Error: cannot create {storage.last_error}") return -2 @@ -99,6 +102,14 @@ class Main(App): storage.send_and_wait_eol( f"update install {flipper_update_path}/{manifest_name}\r" ) + result = storage.read.until(storage.CLI_EOL) + if not b"Verifying" in result: + self.logger.error(f"Unexpected response: {result.decode('ascii')}") + return -4 + result = storage.read.until(storage.CLI_EOL) + if not result.startswith(b"OK"): + self.logger.error(result.decode("ascii")) + return -5 break return 0 finally: