Przeglądaj źródła

[FL-2578] Updater fixes related to /int handling (#1359)

* 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: あく <alleteam@gmail.com>
hedger 3 lat temu
rodzic
commit
b95cd2df14

+ 9 - 3
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) {

+ 1 - 0
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);

+ 3 - 1
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) {

+ 0 - 4
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;

+ 5 - 3
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);

+ 8 - 7
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();

+ 2 - 0
applications/updater/util/update_task_i.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include <storage/storage.h>
+#include <furi_hal.h>
 
 #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);

+ 19 - 16
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);

+ 2 - 0
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;

+ 1 - 1
assets/protobuf

@@ -1 +1 @@
-Subproject commit e3d9cdb66ce789f84f6f8e0bdd6d022187964425
+Subproject commit 6c1b8ae66a85bcd7e79e993a0b5573c38c302db5

+ 11 - 1
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;
         }
 

+ 2 - 0
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);

+ 13 - 2
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: