[ota] DRY NVS reinit on update_partition_table failure paths

Replace the four explicit nvs_flash_init() calls with a small RAII
guard (NvsReinitGuard) declared right after nvs_flash_deinit(). Each
failure path now just returns; the guard reinits NVS in its destructor.
The success path disarms it before the trailing return because the
device reboots immediately afterwards and reinit would only churn the
partition cache.

No behaviour change.
This commit is contained in:
J. Nick Koston
2026-05-03 09:30:23 -05:00
parent b75f5034e5
commit 4dcccf2cdf
+21 -8
View File
@@ -184,6 +184,23 @@ static const esp_partition_t *find_app_partition_at(uint32_t address, size_t min
return found;
}
// RAII helper for the destructive section of update_partition_table(). nvs_flash_deinit() is
// called immediately before the first partition-table write so that earlier failure paths leave
// NVS functional; this guard re-initializes NVS on every early return past that point so any
// component still running after a failed OTA can keep using NVS. The success path disarms the
// guard before returning because the device reboots immediately afterwards and reinit would only
// churn the partition cache.
namespace {
struct NvsReinitGuard {
bool armed{true};
~NvsReinitGuard() {
if (armed) {
nvs_flash_init();
}
}
};
} // namespace
// Validate the new partition table image staged in ``buf_`` and pick the slot the running app
// will boot from after the update. Performs all non-destructive checks; the destructive write
// is in ``update_partition_table()``. Side-effect: registers the live partition-table region
@@ -375,10 +392,10 @@ OTAResponseTypes IDFOTABackend::update_partition_table() {
// Deinitialize NVS just before the first destructive write to the partition-table region. Doing
// this here (instead of earlier) means that any failure path in the verify or copy phases above
// returns with NVS still functional, so other components on the device aren't broken until reboot.
// Each failure path past this point calls nvs_flash_init() before returning so that, if the
// device keeps running, components that depend on NVS aren't permanently broken. The success
// path skips reinit because the device reboots immediately afterwards.
// The RAII guard re-initializes NVS on every early-return below; the success path disarms it
// immediately before returning, since the device reboots right after.
nvs_flash_deinit();
NvsReinitGuard nvs_guard;
// Update the partition table
err = esp_ota_begin(this->partition_table_part_, ESP_PARTITION_TABLE_MAX_LEN, &this->update_handle_);
@@ -386,7 +403,6 @@ OTAResponseTypes IDFOTABackend::update_partition_table() {
esp_ota_abort(this->update_handle_);
this->update_handle_ = 0;
ESP_LOGE(TAG, "esp_ota_begin failed (err=0x%X)", err);
nvs_flash_init();
return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE;
}
err = esp_ota_write(this->update_handle_, this->buf_, ESP_PARTITION_TABLE_MAX_LEN);
@@ -396,14 +412,12 @@ OTAResponseTypes IDFOTABackend::update_partition_table() {
esp_ota_abort(this->update_handle_);
this->update_handle_ = 0;
ESP_LOGE(TAG, "esp_ota_write failed (err=0x%X)", err);
nvs_flash_init();
return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE;
}
err = esp_ota_end(this->update_handle_);
this->update_handle_ = 0; // esp_ota_end releases the handle internally regardless of result
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_end failed (err=0x%X)", err);
nvs_flash_init();
return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE;
}
// esp_partition_unload_all() invalidates every cached partition entry, including the externally
@@ -418,16 +432,15 @@ OTAResponseTypes IDFOTABackend::update_partition_table() {
const esp_partition_t *new_boot_partition = find_app_partition_at(new_part->pos.offset, 0);
if (new_boot_partition == nullptr) {
ESP_LOGE(TAG, "Selected app partition not found after partition table update");
nvs_flash_init();
return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE;
}
ESP_LOGD(TAG, "Setting next boot partition to 0x%X", new_boot_partition->address);
err = esp_ota_set_boot_partition(new_boot_partition);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (err=0x%X)", err);
nvs_flash_init();
return OTA_RESPONSE_ERROR_PARTITION_TABLE_UPDATE;
}
nvs_guard.armed = false;
return OTA_RESPONSE_OK;
}