[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:
parent
4a1695ba1c
commit
b95cd2df14
@ -12,6 +12,7 @@
|
|||||||
struct Slideshow {
|
struct Slideshow {
|
||||||
Icon icon;
|
Icon icon;
|
||||||
uint32_t current_frame;
|
uint32_t current_frame;
|
||||||
|
bool loaded;
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
@ -34,6 +35,7 @@ _Static_assert(sizeof(SlideshowFrameHeader) == 2, "Incorrect SlideshowFrameHeade
|
|||||||
|
|
||||||
Slideshow* slideshow_alloc() {
|
Slideshow* slideshow_alloc() {
|
||||||
Slideshow* ret = malloc(sizeof(Slideshow));
|
Slideshow* ret = malloc(sizeof(Slideshow));
|
||||||
|
ret->loaded = false;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +54,7 @@ void slideshow_free(Slideshow* slideshow) {
|
|||||||
bool slideshow_load(Slideshow* slideshow, const char* fspath) {
|
bool slideshow_load(Slideshow* slideshow, const char* fspath) {
|
||||||
Storage* storage = furi_record_open("storage");
|
Storage* storage = furi_record_open("storage");
|
||||||
File* slideshow_file = storage_file_alloc(storage);
|
File* slideshow_file = storage_file_alloc(storage);
|
||||||
bool load_success = false;
|
slideshow->loaded = false;
|
||||||
do {
|
do {
|
||||||
if(!storage_file_open(slideshow_file, fspath, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
if(!storage_file_open(slideshow_file, fspath, FSAM_READ, FSOM_OPEN_EXISTING)) {
|
||||||
break;
|
break;
|
||||||
@ -80,12 +82,16 @@ bool slideshow_load(Slideshow* slideshow, const char* fspath) {
|
|||||||
frame_header.size) {
|
frame_header.size) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
load_success = (frame_idx + 1) == header.frame_count;
|
slideshow->loaded = (frame_idx + 1) == header.frame_count;
|
||||||
}
|
}
|
||||||
} while(false);
|
} while(false);
|
||||||
storage_file_free(slideshow_file);
|
storage_file_free(slideshow_file);
|
||||||
furi_record_close("storage");
|
furi_record_close("storage");
|
||||||
return load_success;
|
return slideshow->loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool slideshow_is_loaded(Slideshow* slideshow) {
|
||||||
|
return slideshow->loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool slideshow_advance(Slideshow* slideshow) {
|
bool slideshow_advance(Slideshow* slideshow) {
|
||||||
|
@ -8,6 +8,7 @@ Slideshow* slideshow_alloc();
|
|||||||
|
|
||||||
void slideshow_free(Slideshow* slideshow);
|
void slideshow_free(Slideshow* slideshow);
|
||||||
bool slideshow_load(Slideshow* slideshow, const char* fspath);
|
bool slideshow_load(Slideshow* slideshow, const char* fspath);
|
||||||
|
bool slideshow_is_loaded(Slideshow* slideshow);
|
||||||
void slideshow_goback(Slideshow* slideshow);
|
void slideshow_goback(Slideshow* slideshow);
|
||||||
bool slideshow_advance(Slideshow* slideshow);
|
bool slideshow_advance(Slideshow* slideshow);
|
||||||
void slideshow_draw(Slideshow* slideshow, Canvas* canvas, uint8_t x, uint8_t y);
|
void slideshow_draw(Slideshow* slideshow, Canvas* canvas, uint8_t x, uint8_t y);
|
||||||
|
@ -21,7 +21,9 @@ static void desktop_view_slideshow_draw(Canvas* canvas, void* model) {
|
|||||||
DesktopSlideshowViewModel* m = model;
|
DesktopSlideshowViewModel* m = model;
|
||||||
|
|
||||||
canvas_clear(canvas);
|
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) {
|
static bool desktop_view_slideshow_input(InputEvent* event, void* context) {
|
||||||
|
@ -277,10 +277,6 @@ static void rpc_system_system_update_request_process(const PB_Main* request, voi
|
|||||||
|
|
||||||
UpdatePrepareResult update_prepare_result =
|
UpdatePrepareResult update_prepare_result =
|
||||||
update_operation_prepare(request->content.system_update_request.update_manifest);
|
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));
|
PB_Main* response = malloc(sizeof(PB_Main));
|
||||||
response->command_id = request->command_id;
|
response->command_id = request->command_id;
|
||||||
|
@ -655,11 +655,13 @@ static FS_Error storage_int_common_fs_info(
|
|||||||
lfs_t* lfs = lfs_get_from_storage(storage);
|
lfs_t* lfs = lfs_get_from_storage(storage);
|
||||||
LFSData* lfs_data = lfs_data_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);
|
lfs_ssize_t result = lfs_fs_size(lfs);
|
||||||
if(result >= 0) {
|
if(free_space && (result >= 0)) {
|
||||||
*free_space = *total_space - (result * lfs_data->config.block_size);
|
*free_space = (lfs_data->config.block_count - result) * lfs_data->config.block_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return storage_int_parse_error(result);
|
return storage_int_parse_error(result);
|
||||||
|
@ -44,17 +44,17 @@ static const UpdateTaskStageGroupMap update_task_stage_progress[] = {
|
|||||||
[UpdateTaskStageReadManifest] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 5),
|
[UpdateTaskStageReadManifest] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 5),
|
||||||
[UpdateTaskStageLfsBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 15),
|
[UpdateTaskStageLfsBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 15),
|
||||||
|
|
||||||
[UpdateTaskStageRadioImageValidate] = STAGE_DEF(UpdateTaskStageGroupRadio, 10),
|
[UpdateTaskStageRadioImageValidate] = STAGE_DEF(UpdateTaskStageGroupRadio, 15),
|
||||||
[UpdateTaskStageRadioErase] = STAGE_DEF(UpdateTaskStageGroupRadio, 50),
|
[UpdateTaskStageRadioErase] = STAGE_DEF(UpdateTaskStageGroupRadio, 60),
|
||||||
[UpdateTaskStageRadioWrite] = STAGE_DEF(UpdateTaskStageGroupRadio, 90),
|
[UpdateTaskStageRadioWrite] = STAGE_DEF(UpdateTaskStageGroupRadio, 80),
|
||||||
[UpdateTaskStageRadioInstall] = STAGE_DEF(UpdateTaskStageGroupRadio, 15),
|
[UpdateTaskStageRadioInstall] = STAGE_DEF(UpdateTaskStageGroupRadio, 60),
|
||||||
[UpdateTaskStageRadioBusy] = STAGE_DEF(UpdateTaskStageGroupRadio, 60),
|
[UpdateTaskStageRadioBusy] = STAGE_DEF(UpdateTaskStageGroupRadio, 80),
|
||||||
|
|
||||||
[UpdateTaskStageOBValidation] = STAGE_DEF(UpdateTaskStageGroupOptionBytes, 10),
|
[UpdateTaskStageOBValidation] = STAGE_DEF(UpdateTaskStageGroupOptionBytes, 10),
|
||||||
|
|
||||||
[UpdateTaskStageValidateDFUImage] = STAGE_DEF(UpdateTaskStageGroupFirmware, 100),
|
[UpdateTaskStageValidateDFUImage] = STAGE_DEF(UpdateTaskStageGroupFirmware, 50),
|
||||||
[UpdateTaskStageFlashWrite] = STAGE_DEF(UpdateTaskStageGroupFirmware, 200),
|
[UpdateTaskStageFlashWrite] = STAGE_DEF(UpdateTaskStageGroupFirmware, 200),
|
||||||
[UpdateTaskStageFlashValidate] = STAGE_DEF(UpdateTaskStageGroupFirmware, 50),
|
[UpdateTaskStageFlashValidate] = STAGE_DEF(UpdateTaskStageGroupFirmware, 30),
|
||||||
|
|
||||||
[UpdateTaskStageLfsRestore] = STAGE_DEF(UpdateTaskStageGroupPostUpdate, 30),
|
[UpdateTaskStageLfsRestore] = STAGE_DEF(UpdateTaskStageGroupPostUpdate, 30),
|
||||||
|
|
||||||
@ -214,6 +214,7 @@ UpdateTask* update_task_alloc() {
|
|||||||
update_task->storage = furi_record_open("storage");
|
update_task->storage = furi_record_open("storage");
|
||||||
update_task->file = storage_file_alloc(update_task->storage);
|
update_task->file = storage_file_alloc(update_task->storage);
|
||||||
update_task->status_change_cb = NULL;
|
update_task->status_change_cb = NULL;
|
||||||
|
update_task->boot_mode = furi_hal_rtc_get_boot_mode();
|
||||||
string_init(update_task->update_path);
|
string_init(update_task->update_path);
|
||||||
|
|
||||||
FuriThread* thread = update_task->thread = furi_thread_alloc();
|
FuriThread* thread = update_task->thread = furi_thread_alloc();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
|
||||||
#define UPDATE_TASK_NOERR 0
|
#define UPDATE_TASK_NOERR 0
|
||||||
#define UPDATE_TASK_FAILED -1
|
#define UPDATE_TASK_FAILED -1
|
||||||
@ -14,6 +15,7 @@ typedef struct UpdateTask {
|
|||||||
File* file;
|
File* file;
|
||||||
updateProgressCb status_change_cb;
|
updateProgressCb status_change_cb;
|
||||||
void* status_change_cb_state;
|
void* status_change_cb_state;
|
||||||
|
FuriHalRtcBootMode boot_mode;
|
||||||
} UpdateTask;
|
} UpdateTask;
|
||||||
|
|
||||||
void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress);
|
void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress);
|
||||||
|
@ -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);
|
string_get_cstr(update_task->update_path), LFS_BACKUP_DEFAULT_FILENAME, file_path);
|
||||||
|
|
||||||
update_task_set_progress(update_task, UpdateTaskStageLfsRestore, 0);
|
update_task_set_progress(update_task, UpdateTaskStageLfsRestore, 0);
|
||||||
update_operation_disarm();
|
|
||||||
|
|
||||||
CHECK_RESULT(lfs_backup_unpack(update_task->storage, string_get_cstr(file_path)));
|
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) {
|
int32_t update_task_worker_backup_restore(void* context) {
|
||||||
furi_assert(context);
|
furi_assert(context);
|
||||||
UpdateTask* update_task = 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)) {
|
if((boot_mode != FuriHalRtcBootModePreUpdate) && (boot_mode != FuriHalRtcBootModePostUpdate)) {
|
||||||
/* no idea how we got here. Clear to normal boot */
|
/* no idea how we got here. Do nothing */
|
||||||
update_operation_disarm();
|
|
||||||
return UPDATE_TASK_NOERR;
|
return UPDATE_TASK_NOERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!update_task_parse_manifest(update_task)) {
|
bool success = false;
|
||||||
return UPDATE_TASK_FAILED;
|
do {
|
||||||
}
|
if(!update_task_parse_manifest(update_task)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* Waiting for BT service to 'start', so we don't race for boot mode flag */
|
/* Waiting for BT service to 'start', so we don't race for boot mode flag */
|
||||||
furi_record_open("bt");
|
furi_record_open("bt");
|
||||||
furi_record_close("bt");
|
furi_record_close("bt");
|
||||||
|
|
||||||
if(boot_mode == FuriHalRtcBootModePreUpdate) {
|
if(boot_mode == FuriHalRtcBootModePreUpdate) {
|
||||||
success = update_task_pre_update(update_task);
|
success = update_task_pre_update(update_task);
|
||||||
} else if(boot_mode == FuriHalRtcBootModePostUpdate) {
|
} else if(boot_mode == FuriHalRtcBootModePostUpdate) {
|
||||||
success = update_task_post_update(update_task);
|
success = update_task_post_update(update_task);
|
||||||
}
|
if(success) {
|
||||||
|
update_operation_disarm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while(false);
|
||||||
|
|
||||||
if(!success) {
|
if(!success) {
|
||||||
update_task_set_progress(update_task, UpdateTaskStageError, 0);
|
update_task_set_progress(update_task, UpdateTaskStageError, 0);
|
||||||
|
@ -340,6 +340,8 @@ int32_t update_task_worker_flash_writer(void* context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate);
|
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);
|
update_task_set_progress(update_task, UpdateTaskStageCompleted, 100);
|
||||||
success = true;
|
success = true;
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit e3d9cdb66ce789f84f6f8e0bdd6d022187964425
|
Subproject commit 6c1b8ae66a85bcd7e79e993a0b5573c38c302db5
|
@ -12,6 +12,8 @@
|
|||||||
#define UPDATE_ROOT_DIR "/ext" UPDATE_DIR_DEFAULT_REL_PATH
|
#define UPDATE_ROOT_DIR "/ext" UPDATE_DIR_DEFAULT_REL_PATH
|
||||||
#define UPDATE_PREFIX "/ext" UPDATE_DIR_DEFAULT_REL_PATH "/"
|
#define UPDATE_PREFIX "/ext" UPDATE_DIR_DEFAULT_REL_PATH "/"
|
||||||
#define UPDATE_SUFFIX "/" UPDATE_MANIFEST_DEFAULT_NAME
|
#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[] = {
|
static const char* update_prepare_result_descr[] = {
|
||||||
[UpdatePrepareResultOK] = "OK",
|
[UpdatePrepareResultOK] = "OK",
|
||||||
@ -22,6 +24,7 @@ static const char* update_prepare_result_descr[] = {
|
|||||||
[UpdatePrepareResultStageIntegrityError] = "Corrupted Stage2 loader",
|
[UpdatePrepareResultStageIntegrityError] = "Corrupted Stage2 loader",
|
||||||
[UpdatePrepareResultManifestPointerError] = "Failed to create update pointer file",
|
[UpdatePrepareResultManifestPointerError] = "Failed to create update pointer file",
|
||||||
[UpdatePrepareResultOutdatedManifestVersion] = "Update package is too old",
|
[UpdatePrepareResultOutdatedManifestVersion] = "Update package is too old",
|
||||||
|
[UpdatePrepareResultIntFull] = "Need more free space in internal storage",
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* update_operation_describe_preparation_result(const UpdatePrepareResult value) {
|
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 update_operation_prepare(const char* manifest_file_path) {
|
||||||
UpdatePrepareResult result = UpdatePrepareResultManifestFolderNotFound;
|
UpdatePrepareResult result = UpdatePrepareResultIntFull;
|
||||||
Storage* storage = furi_record_open("storage");
|
Storage* storage = furi_record_open("storage");
|
||||||
UpdateManifest* manifest = update_manifest_alloc();
|
UpdateManifest* manifest = update_manifest_alloc();
|
||||||
File* file = storage_file_alloc(storage);
|
File* file = storage_file_alloc(storage);
|
||||||
|
|
||||||
|
uint64_t free_int_space;
|
||||||
string_t stage_path;
|
string_t stage_path;
|
||||||
string_init(stage_path);
|
string_init(stage_path);
|
||||||
do {
|
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) {
|
if(storage_common_stat(storage, manifest_file_path, NULL) != FSE_OK) {
|
||||||
|
result = UpdatePrepareResultManifestFolderNotFound;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,8 @@ typedef enum {
|
|||||||
UpdatePrepareResultManifestPointerError,
|
UpdatePrepareResultManifestPointerError,
|
||||||
UpdatePrepareResultTargetMismatch,
|
UpdatePrepareResultTargetMismatch,
|
||||||
UpdatePrepareResultOutdatedManifestVersion,
|
UpdatePrepareResultOutdatedManifestVersion,
|
||||||
|
UpdatePrepareResultIntFull,
|
||||||
|
UpdatePrepareResultUnspecifiedError,
|
||||||
} UpdatePrepareResult;
|
} UpdatePrepareResult;
|
||||||
|
|
||||||
const char* update_operation_describe_preparation_result(const UpdatePrepareResult value);
|
const char* update_operation_describe_preparation_result(const UpdatePrepareResult value);
|
||||||
|
@ -76,12 +76,15 @@ class Main(App):
|
|||||||
manifest_name, pkg_name = manifest_path.parts[-1], manifest_path.parts[-2]
|
manifest_name, pkg_name = manifest_path.parts[-1], manifest_path.parts[-2]
|
||||||
|
|
||||||
pkg_dir_name = self.args.pkg_dir_name or pkg_name
|
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}')
|
self.logger.info(f'Installing "{pkg_name}" from {flipper_update_path}')
|
||||||
# if not os.path.exists(self.args.manifest_path):
|
# if not os.path.exists(self.args.manifest_path):
|
||||||
# self.logger.error("Error: package not found")
|
# 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}")
|
self.logger.error(f"Error: cannot create {storage.last_error}")
|
||||||
return -2
|
return -2
|
||||||
|
|
||||||
@ -99,6 +102,14 @@ class Main(App):
|
|||||||
storage.send_and_wait_eol(
|
storage.send_and_wait_eol(
|
||||||
f"update install {flipper_update_path}/{manifest_name}\r"
|
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
|
break
|
||||||
return 0
|
return 0
|
||||||
finally:
|
finally:
|
||||||
|
Loading…
Reference in New Issue
Block a user