[FL-2527] Updater: Migrating to new manifest path convention (#1213)
* Updater: Migrating to new manifest path convention * RPC: Added update preparation status to RPC * RPC: bumped protobuf submodule * Bumped protobuf_version.h * FuriCore: add missing include. Lib: make mlib smaller * Explicitly tell where we have doubles and fix random in animations * makefile: added -DLFS_NO_DEBUG * Updater: path len constant dedup * Updater: checking for hardware version match before parsing manifest * LD: moved _DRIVER_CONTEXT sections to .bss, where they belong. * LD: avoiding PROBGITS warning, moved _CONTEXT to data * Updater: Added version check on update package - refusing to install outdated Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		@@ -276,7 +276,7 @@ bool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteDa
 | 
			
		||||
#ifndef FLIPPER_STREAM_LITE
 | 
			
		||||
                case FlipperStreamValueFloat: {
 | 
			
		||||
                    const float* data = write_data->data;
 | 
			
		||||
                    string_printf(value, "%f", data[i]);
 | 
			
		||||
                    string_printf(value, "%f", (double)data[i]);
 | 
			
		||||
                }; break;
 | 
			
		||||
#endif
 | 
			
		||||
                case FlipperStreamValueInt32: {
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ LIB_DIR			= $(PROJECT_ROOT)/lib
 | 
			
		||||
CFLAGS			+= -I$(LIB_DIR)
 | 
			
		||||
 | 
			
		||||
# Mlib containers
 | 
			
		||||
CFLAGS			+= -I$(LIB_DIR)/mlib
 | 
			
		||||
CFLAGS			+= -I$(LIB_DIR)/mlib -D'M_MEMORY_FULL(x)=abort()'
 | 
			
		||||
 | 
			
		||||
# U8G2 display library
 | 
			
		||||
U8G2_DIR		= $(LIB_DIR)/u8g2
 | 
			
		||||
 
 | 
			
		||||
@@ -83,7 +83,10 @@ bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t*
 | 
			
		||||
 | 
			
		||||
    if(furi_hal_subghz_rx_pipe_not_empty()) {
 | 
			
		||||
        FURI_LOG_I(
 | 
			
		||||
            TAG, "RSSI: %03.1fdbm LQI: %d", furi_hal_subghz_get_rssi(), furi_hal_subghz_get_lqi());
 | 
			
		||||
            TAG,
 | 
			
		||||
            "RSSI: %03.1fdbm LQI: %d",
 | 
			
		||||
            (double)furi_hal_subghz_get_rssi(),
 | 
			
		||||
            furi_hal_subghz_get_lqi());
 | 
			
		||||
        if(furi_hal_subghz_is_rx_data_crc_valid()) {
 | 
			
		||||
            furi_hal_subghz_read_packet(data, size);
 | 
			
		||||
            ret = true;
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,7 @@ UpdateManifest* update_manifest_alloc() {
 | 
			
		||||
    string_init(update_manifest->staged_loader_file);
 | 
			
		||||
    string_init(update_manifest->resource_bundle);
 | 
			
		||||
    update_manifest->target = 0;
 | 
			
		||||
    update_manifest->manifest_version = 0;
 | 
			
		||||
    memset(update_manifest->ob_reference.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES);
 | 
			
		||||
    memset(update_manifest->ob_compare_mask.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES);
 | 
			
		||||
    memset(update_manifest->ob_write_mask.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES);
 | 
			
		||||
@@ -49,12 +50,11 @@ static bool
 | 
			
		||||
    furi_assert(flipper_file);
 | 
			
		||||
 | 
			
		||||
    string_t filetype;
 | 
			
		||||
    uint32_t version = 0;
 | 
			
		||||
 | 
			
		||||
    // TODO: compare filetype?
 | 
			
		||||
    string_init(filetype);
 | 
			
		||||
    update_manifest->valid =
 | 
			
		||||
        flipper_format_read_header(flipper_file, filetype, &version) &&
 | 
			
		||||
        flipper_format_read_header(flipper_file, filetype, &update_manifest->manifest_version) &&
 | 
			
		||||
        flipper_format_read_string(flipper_file, MANIFEST_KEY_INFO, update_manifest->version) &&
 | 
			
		||||
        flipper_format_read_uint32(
 | 
			
		||||
            flipper_file, MANIFEST_KEY_TARGET, &update_manifest->target, 1) &&
 | 
			
		||||
@@ -68,7 +68,7 @@ static bool
 | 
			
		||||
    string_clear(filetype);
 | 
			
		||||
 | 
			
		||||
    if(update_manifest->valid) {
 | 
			
		||||
        /* Optional fields - we can have dfu, radio, or both */
 | 
			
		||||
        /* Optional fields - we can have dfu, radio, resources, or any combination */
 | 
			
		||||
        flipper_format_read_string(
 | 
			
		||||
            flipper_file, MANIFEST_KEY_DFU_FILE, update_manifest->firmware_dfu_image);
 | 
			
		||||
        flipper_format_read_string(
 | 
			
		||||
@@ -131,8 +131,7 @@ static bool ob_data_check_masked_values_valid(
 | 
			
		||||
    const FuriHalFlashRawOptionByteData* mask) {
 | 
			
		||||
    bool valid = true;
 | 
			
		||||
    for(size_t idx = 0; valid && (idx < FURI_HAL_FLASH_OB_TOTAL_VALUES); ++idx) {
 | 
			
		||||
        valid &= (data->obs[idx]. dword & mask->obs[idx].dword) ==
 | 
			
		||||
                 data->obs[idx].dword;
 | 
			
		||||
        valid &= (data->obs[idx].dword & mask->obs[idx].dword) == data->obs[idx].dword;
 | 
			
		||||
    }
 | 
			
		||||
    return valid;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ extern "C" {
 | 
			
		||||
/* Paths don't include /ext -- because at startup SD card is mounted as root */
 | 
			
		||||
#define UPDATE_DIR_DEFAULT_REL_PATH "/update"
 | 
			
		||||
#define UPDATE_MANIFEST_DEFAULT_NAME "update.fuf"
 | 
			
		||||
#define UPDATE_MANIFEST_POINTER_FILE_NAME ".fupdate"
 | 
			
		||||
 | 
			
		||||
typedef union {
 | 
			
		||||
    uint8_t raw[6];
 | 
			
		||||
@@ -27,6 +28,7 @@ typedef union {
 | 
			
		||||
_Static_assert(sizeof(UpdateManifestRadioVersion) == 6, "UpdateManifestRadioVersion size error");
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    uint32_t manifest_version;
 | 
			
		||||
    string_t version;
 | 
			
		||||
    uint32_t target;
 | 
			
		||||
    string_t staged_loader_file;
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,6 @@
 | 
			
		||||
#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
 | 
			
		||||
#define MAX_DIR_NAME_LEN 250
 | 
			
		||||
 | 
			
		||||
static const char* update_prepare_result_descr[] = {
 | 
			
		||||
    [UpdatePrepareResultOK] = "OK",
 | 
			
		||||
@@ -21,6 +20,8 @@ static const char* update_prepare_result_descr[] = {
 | 
			
		||||
    [UpdatePrepareResultManifestInvalid] = "Invalid manifest data",
 | 
			
		||||
    [UpdatePrepareResultStageMissing] = "Missing Stage2 loader",
 | 
			
		||||
    [UpdatePrepareResultStageIntegrityError] = "Corrupted Stage2 loader",
 | 
			
		||||
    [UpdatePrepareResultManifestPointerError] = "Failed to create update pointer file",
 | 
			
		||||
    [UpdatePrepareResultOutdatedManifestVersion] = "Update package is too old",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const char* update_operation_describe_preparation_result(const UpdatePrepareResult value) {
 | 
			
		||||
@@ -31,65 +32,7 @@ const char* update_operation_describe_preparation_result(const UpdatePrepareResu
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool update_operation_get_package_dir_name(const char* full_path, string_t out_manifest_dir) {
 | 
			
		||||
    bool path_ok = false;
 | 
			
		||||
    string_t full_path_str;
 | 
			
		||||
    string_init_set(full_path_str, full_path);
 | 
			
		||||
    string_reset(out_manifest_dir);
 | 
			
		||||
    bool start_end_ok = string_start_with_str_p(full_path_str, UPDATE_PREFIX) &&
 | 
			
		||||
                        string_end_with_str_p(full_path_str, UPDATE_SUFFIX);
 | 
			
		||||
    int16_t dir_name_len =
 | 
			
		||||
        strlen(full_path) - strlen(UPDATE_PREFIX) - strlen(UPDATE_MANIFEST_DEFAULT_NAME) - 1;
 | 
			
		||||
    if(dir_name_len == -1) {
 | 
			
		||||
        path_ok = true;
 | 
			
		||||
    } else if(start_end_ok && (dir_name_len > 0)) {
 | 
			
		||||
        string_set_n(out_manifest_dir, full_path_str, strlen(UPDATE_PREFIX), dir_name_len);
 | 
			
		||||
        path_ok = true;
 | 
			
		||||
        if(string_search_char(out_manifest_dir, '/') != STRING_FAILURE) {
 | 
			
		||||
            string_reset(out_manifest_dir);
 | 
			
		||||
            path_ok = false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    string_clear(full_path_str);
 | 
			
		||||
    return path_ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int32_t update_operation_get_package_index(Storage* storage, const char* update_package_dir) {
 | 
			
		||||
    furi_assert(storage);
 | 
			
		||||
    furi_assert(update_package_dir);
 | 
			
		||||
 | 
			
		||||
    if(strlen(update_package_dir) == 0) {
 | 
			
		||||
        return UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool found = false;
 | 
			
		||||
    int32_t index = 0;
 | 
			
		||||
    File* dir = storage_file_alloc(storage);
 | 
			
		||||
    FileInfo fi = {0};
 | 
			
		||||
    char* name_buffer = malloc(MAX_DIR_NAME_LEN);
 | 
			
		||||
    do {
 | 
			
		||||
        if(!storage_dir_open(dir, UPDATE_ROOT_DIR)) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        while(storage_dir_read(dir, &fi, name_buffer, MAX_DIR_NAME_LEN)) {
 | 
			
		||||
            index++;
 | 
			
		||||
            if(strcmp(name_buffer, update_package_dir)) {
 | 
			
		||||
                continue;
 | 
			
		||||
            } else {
 | 
			
		||||
                found = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } while(false);
 | 
			
		||||
 | 
			
		||||
    free(name_buffer);
 | 
			
		||||
    storage_file_free(dir);
 | 
			
		||||
 | 
			
		||||
    return found ? index : -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool update_operation_get_current_package_path(Storage* storage, string_t out_path) {
 | 
			
		||||
static bool update_operation_get_current_package_path_rtc(Storage* storage, string_t out_path) {
 | 
			
		||||
    const uint32_t update_index = furi_hal_rtc_get_register(FuriHalRtcRegisterUpdateFolderFSIndex);
 | 
			
		||||
    string_set_str(out_path, UPDATE_ROOT_DIR);
 | 
			
		||||
    if(update_index == UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC) {
 | 
			
		||||
@@ -100,13 +43,13 @@ bool update_operation_get_current_package_path(Storage* storage, string_t out_pa
 | 
			
		||||
    uint32_t iter_index = 0;
 | 
			
		||||
    File* dir = storage_file_alloc(storage);
 | 
			
		||||
    FileInfo fi = {0};
 | 
			
		||||
    char* name_buffer = malloc(MAX_DIR_NAME_LEN);
 | 
			
		||||
    char* name_buffer = malloc(UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN);
 | 
			
		||||
    do {
 | 
			
		||||
        if(!storage_dir_open(dir, UPDATE_ROOT_DIR)) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        while(storage_dir_read(dir, &fi, name_buffer, MAX_DIR_NAME_LEN)) {
 | 
			
		||||
        while(storage_dir_read(dir, &fi, name_buffer, UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN)) {
 | 
			
		||||
            if(++iter_index == update_index) {
 | 
			
		||||
                found = true;
 | 
			
		||||
                path_append(out_path, name_buffer);
 | 
			
		||||
@@ -124,79 +67,148 @@ bool update_operation_get_current_package_path(Storage* storage, string_t out_pa
 | 
			
		||||
    return found;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define UPDATE_FILE_POINTER_FN "/ext/" UPDATE_MANIFEST_POINTER_FILE_NAME
 | 
			
		||||
#define UPDATE_MANIFEST_MAX_PATH_LEN 256u
 | 
			
		||||
 | 
			
		||||
bool update_operation_get_current_package_manifest_path(Storage* storage, string_t out_path) {
 | 
			
		||||
    string_reset(out_path);
 | 
			
		||||
    if(storage_common_stat(storage, UPDATE_FILE_POINTER_FN, NULL) == FSE_OK) {
 | 
			
		||||
        char* manifest_name_buffer = malloc(UPDATE_MANIFEST_MAX_PATH_LEN);
 | 
			
		||||
        File* upd_file = NULL;
 | 
			
		||||
        do {
 | 
			
		||||
            upd_file = storage_file_alloc(storage);
 | 
			
		||||
            if(!storage_file_open(
 | 
			
		||||
                   upd_file, UPDATE_FILE_POINTER_FN, FSAM_READ, FSOM_OPEN_EXISTING)) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            uint16_t bytes_read =
 | 
			
		||||
                storage_file_read(upd_file, manifest_name_buffer, UPDATE_MANIFEST_MAX_PATH_LEN);
 | 
			
		||||
            if((bytes_read == 0) || (bytes_read == UPDATE_MANIFEST_MAX_PATH_LEN)) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if(storage_common_stat(storage, manifest_name_buffer, NULL) != FSE_OK) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            string_set_str(out_path, manifest_name_buffer);
 | 
			
		||||
        } while(0);
 | 
			
		||||
        free(manifest_name_buffer);
 | 
			
		||||
        storage_file_free(upd_file);
 | 
			
		||||
    } else {
 | 
			
		||||
        /* legacy, will be deprecated */
 | 
			
		||||
        string_t rtcpath;
 | 
			
		||||
        string_init(rtcpath);
 | 
			
		||||
        do {
 | 
			
		||||
            if(!update_operation_get_current_package_path_rtc(storage, rtcpath)) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            path_concat(string_get_cstr(rtcpath), UPDATE_MANIFEST_DEFAULT_NAME, out_path);
 | 
			
		||||
        } while(0);
 | 
			
		||||
        string_clear(rtcpath);
 | 
			
		||||
    }
 | 
			
		||||
    return !string_empty_p(out_path);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool update_operation_persist_manifest_path(Storage* storage, const char* manifest_path) {
 | 
			
		||||
    const uint16_t manifest_path_len = strlen(manifest_path);
 | 
			
		||||
    furi_check(manifest_path && manifest_path_len);
 | 
			
		||||
    bool success = false;
 | 
			
		||||
    File* file = storage_file_alloc(storage);
 | 
			
		||||
    do {
 | 
			
		||||
        if(manifest_path_len >= UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(!storage_file_open(file, UPDATE_FILE_POINTER_FN, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(storage_file_write(file, manifest_path, manifest_path_len) != manifest_path_len) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        success = true;
 | 
			
		||||
    } while(0);
 | 
			
		||||
    storage_file_free(file);
 | 
			
		||||
    return success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) {
 | 
			
		||||
    string_t update_folder;
 | 
			
		||||
    string_init(update_folder);
 | 
			
		||||
    if(!update_operation_get_package_dir_name(manifest_file_path, update_folder)) {
 | 
			
		||||
        string_clear(update_folder);
 | 
			
		||||
        return UpdatePrepareResultManifestPathInvalid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    UpdatePrepareResult result = UpdatePrepareResultManifestFolderNotFound;
 | 
			
		||||
    Storage* storage = furi_record_open("storage");
 | 
			
		||||
    int32_t update_index =
 | 
			
		||||
        update_operation_get_package_index(storage, string_get_cstr(update_folder));
 | 
			
		||||
    string_clear(update_folder);
 | 
			
		||||
 | 
			
		||||
    if(update_index < 0) {
 | 
			
		||||
        furi_record_close("storage");
 | 
			
		||||
        return UpdatePrepareResultManifestFolderNotFound;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    string_t update_dir_path;
 | 
			
		||||
    string_init(update_dir_path);
 | 
			
		||||
    path_extract_dirname(manifest_file_path, update_dir_path);
 | 
			
		||||
 | 
			
		||||
    UpdatePrepareResult result = UpdatePrepareResultManifestInvalid;
 | 
			
		||||
    UpdateManifest* manifest = update_manifest_alloc();
 | 
			
		||||
    if(update_manifest_init(manifest, manifest_file_path)) {
 | 
			
		||||
        result = UpdatePrepareResultStageMissing;
 | 
			
		||||
        File* file = storage_file_alloc(storage);
 | 
			
		||||
    File* file = storage_file_alloc(storage);
 | 
			
		||||
 | 
			
		||||
    string_t stage_path;
 | 
			
		||||
    string_init(stage_path);
 | 
			
		||||
    do {
 | 
			
		||||
        if(storage_common_stat(storage, manifest_file_path, NULL) != FSE_OK) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(!update_manifest_init(manifest, manifest_file_path)) {
 | 
			
		||||
            result = UpdatePrepareResultManifestInvalid;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(manifest->manifest_version < UPDATE_OPERATION_MIN_MANIFEST_VERSION) {
 | 
			
		||||
            result = UpdatePrepareResultOutdatedManifestVersion;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(furi_hal_version_get_hw_target() != manifest->target) {
 | 
			
		||||
            result = UpdatePrepareResultTargetMismatch;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        string_t stage_path;
 | 
			
		||||
        string_init(stage_path);
 | 
			
		||||
        path_extract_dirname(manifest_file_path, stage_path);
 | 
			
		||||
        path_append(stage_path, string_get_cstr(manifest->staged_loader_file));
 | 
			
		||||
 | 
			
		||||
        uint32_t crc = 0;
 | 
			
		||||
        do {
 | 
			
		||||
            if(!storage_file_open(
 | 
			
		||||
                   file, string_get_cstr(stage_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            result = UpdatePrepareResultStageIntegrityError;
 | 
			
		||||
            crc = crc32_calc_file(file, NULL, NULL);
 | 
			
		||||
        } while(false);
 | 
			
		||||
 | 
			
		||||
        string_clear(stage_path);
 | 
			
		||||
        storage_file_free(file);
 | 
			
		||||
 | 
			
		||||
        if(crc == manifest->staged_loader_crc) {
 | 
			
		||||
            furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePreUpdate);
 | 
			
		||||
            update_operation_persist_package_index(update_index);
 | 
			
		||||
            result = UpdatePrepareResultOK;
 | 
			
		||||
        if(!storage_file_open(file, string_get_cstr(stage_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
 | 
			
		||||
            result = UpdatePrepareResultStageMissing;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    furi_record_close("storage");
 | 
			
		||||
 | 
			
		||||
        uint32_t crc = crc32_calc_file(file, NULL, NULL);
 | 
			
		||||
        if(crc != manifest->staged_loader_crc) {
 | 
			
		||||
            result = UpdatePrepareResultStageIntegrityError;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(!update_operation_persist_manifest_path(storage, manifest_file_path)) {
 | 
			
		||||
            result = UpdatePrepareResultManifestPointerError;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        result = UpdatePrepareResultOK;
 | 
			
		||||
        furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePreUpdate);
 | 
			
		||||
    } while(false);
 | 
			
		||||
 | 
			
		||||
    string_clear(stage_path);
 | 
			
		||||
    storage_file_free(file);
 | 
			
		||||
 | 
			
		||||
    update_manifest_free(manifest);
 | 
			
		||||
    furi_record_close("storage");
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool update_operation_is_armed() {
 | 
			
		||||
    FuriHalRtcBootMode boot_mode = furi_hal_rtc_get_boot_mode();
 | 
			
		||||
    const uint32_t rtc_upd_index =
 | 
			
		||||
        furi_hal_rtc_get_register(FuriHalRtcRegisterUpdateFolderFSIndex);
 | 
			
		||||
    Storage* storage = furi_record_open("storage");
 | 
			
		||||
    const bool upd_fn_ptr_exists =
 | 
			
		||||
        (storage_common_stat(storage, UPDATE_FILE_POINTER_FN, NULL) == FSE_OK);
 | 
			
		||||
    furi_record_close("storage");
 | 
			
		||||
    return (boot_mode >= FuriHalRtcBootModePreUpdate) &&
 | 
			
		||||
           (boot_mode <= FuriHalRtcBootModePostUpdate) &&
 | 
			
		||||
           (furi_hal_rtc_get_register(FuriHalRtcRegisterUpdateFolderFSIndex) > 0);
 | 
			
		||||
           ((rtc_upd_index != INT_MAX) || upd_fn_ptr_exists);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void update_operation_disarm() {
 | 
			
		||||
    furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal);
 | 
			
		||||
    furi_hal_rtc_set_register(
 | 
			
		||||
        FuriHalRtcRegisterUpdateFolderFSIndex, INT_MAX);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void update_operation_persist_package_index(int32_t index) {
 | 
			
		||||
    furi_check(index >= 0);
 | 
			
		||||
    furi_hal_rtc_set_register(FuriHalRtcRegisterUpdateFolderFSIndex, index);
 | 
			
		||||
}
 | 
			
		||||
    furi_hal_rtc_set_register(FuriHalRtcRegisterUpdateFolderFSIndex, INT_MAX);
 | 
			
		||||
    Storage* storage = furi_record_open("storage");
 | 
			
		||||
    storage_simply_remove(storage, UPDATE_FILE_POINTER_FN);
 | 
			
		||||
    furi_record_close("storage");
 | 
			
		||||
}
 | 
			
		||||
@@ -9,6 +9,8 @@ extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC 0
 | 
			
		||||
#define UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN 255u
 | 
			
		||||
#define UPDATE_OPERATION_MIN_MANIFEST_VERSION 2
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Checks if supplied full manifest path is valid
 | 
			
		||||
@@ -19,6 +21,7 @@ extern "C" {
 | 
			
		||||
 */
 | 
			
		||||
bool update_operation_get_package_dir_name(const char* full_path, string_t out_manifest_dir);
 | 
			
		||||
 | 
			
		||||
/* When updating this enum, also update assets/protobuf/system.proto */
 | 
			
		||||
typedef enum {
 | 
			
		||||
    UpdatePrepareResultOK,
 | 
			
		||||
    UpdatePrepareResultManifestPathInvalid,
 | 
			
		||||
@@ -26,6 +29,9 @@ typedef enum {
 | 
			
		||||
    UpdatePrepareResultManifestInvalid,
 | 
			
		||||
    UpdatePrepareResultStageMissing,
 | 
			
		||||
    UpdatePrepareResultStageIntegrityError,
 | 
			
		||||
    UpdatePrepareResultManifestPointerError,
 | 
			
		||||
    UpdatePrepareResultTargetMismatch,
 | 
			
		||||
    UpdatePrepareResultOutdatedManifestVersion,
 | 
			
		||||
} UpdatePrepareResult;
 | 
			
		||||
 | 
			
		||||
const char* update_operation_describe_preparation_result(const UpdatePrepareResult value);
 | 
			
		||||
@@ -37,27 +43,13 @@ const char* update_operation_describe_preparation_result(const UpdatePrepareResu
 | 
			
		||||
 */
 | 
			
		||||
UpdatePrepareResult update_operation_prepare(const char* manifest_file_path);
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Gets update package index to pass in RTC registers
 | 
			
		||||
 * @param storage Storage API
 | 
			
		||||
 * @param update_package_dir Package directory name
 | 
			
		||||
 * @return int32_t <=0 - error, >0 - update index value
 | 
			
		||||
 */
 | 
			
		||||
int32_t update_operation_get_package_index(Storage* storage, const char* update_package_dir);
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Gets filesystem path for current update package
 | 
			
		||||
 * @param storage Storage API
 | 
			
		||||
 * @param out_path Path to directory with manifest & related files. Must be initialized
 | 
			
		||||
 * @param out_path Path to manifest. Must be initialized
 | 
			
		||||
 * @return true if path was restored successfully
 | 
			
		||||
 */
 | 
			
		||||
bool update_operation_get_current_package_path(Storage* storage, string_t out_path);
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Stores given update index in RTC registers
 | 
			
		||||
 * @param index Value to store
 | 
			
		||||
 */
 | 
			
		||||
void update_operation_persist_package_index(int32_t index);
 | 
			
		||||
bool update_operation_get_current_package_manifest_path(Storage* storage, string_t out_path);
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
 * Checks if an update operation step is pending after reset
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user