[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>
This commit is contained in:
hedger 2022-07-04 16:36:12 +03:00 committed by GitHub
parent 4a1695ba1c
commit b95cd2df14
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 76 additions and 38 deletions

View File

@ -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) {

View File

@ -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);

View File

@ -21,8 +21,10 @@ static void desktop_view_slideshow_draw(Canvas* canvas, void* model) {
DesktopSlideshowViewModel* m = model;
canvas_clear(canvas);
if(slideshow_is_loaded(m->slideshow)) {
slideshow_draw(m->slideshow, canvas, 0, 0);
}
}
static bool desktop_view_slideshow_input(InputEvent* event, void* context) {
furi_assert(event);

View File

@ -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;

View File

@ -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);
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);

View File

@ -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();

View File

@ -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);

View File

@ -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,17 +116,17 @@ 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;
}
bool success = false;
do {
if(!update_task_parse_manifest(update_task)) {
return UPDATE_TASK_FAILED;
break;
}
/* Waiting for BT service to 'start', so we don't race for boot mode flag */
@ -138,7 +137,11 @@ int32_t update_task_worker_backup_restore(void* context) {
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);

View File

@ -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 @@
Subproject commit e3d9cdb66ce789f84f6f8e0bdd6d022187964425
Subproject commit 6c1b8ae66a85bcd7e79e993a0b5573c38c302db5

View File

@ -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;
}

View File

@ -32,6 +32,8 @@ typedef enum {
UpdatePrepareResultManifestPointerError,
UpdatePrepareResultTargetMismatch,
UpdatePrepareResultOutdatedManifestVersion,
UpdatePrepareResultIntFull,
UpdatePrepareResultUnspecifiedError,
} UpdatePrepareResult;
const char* update_operation_describe_preparation_result(const UpdatePrepareResult value);

View File

@ -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: