diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index ee95cc2510..8d643ea41e 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -19,6 +19,7 @@ #include "nvs_partition_manager.hpp" #include "esp_partition.h" #include "sdkconfig.h" +#include #include "nvs_handle_simple.hpp" #ifdef CONFIG_NVS_ENCRYPTION #include "nvs_encr.hpp" @@ -120,8 +121,16 @@ extern "C" esp_err_t nvs_flash_secure_init_custom(const char *partName, uint32_t static esp_err_t close_handles_and_deinit(const char* part_name) { - // Delete all corresponding open handles - s_nvs_handles.clearAndFreeNodes(); + auto belongs_to_part = [=](NVSHandleEntry& e) -> bool { + return strncmp(e.nvs_handle->get_partition_name(), part_name, NVS_PART_NAME_MAX_SIZE) == 0; + }; + + auto it = find_if(begin(s_nvs_handles), end(s_nvs_handles), belongs_to_part); + + while (it != end(s_nvs_handles)) { + s_nvs_handles.erase(it); + it = find_if(begin(s_nvs_handles), end(s_nvs_handles), belongs_to_part); + } // Deinit partition return NVSPartitionManager::get_instance()->deinit_partition(part_name); diff --git a/components/nvs_flash/src/nvs_handle_simple.cpp b/components/nvs_flash/src/nvs_handle_simple.cpp index 744dd80fb4..348e197b7c 100644 --- a/components/nvs_flash/src/nvs_handle_simple.cpp +++ b/components/nvs_flash/src/nvs_handle_simple.cpp @@ -130,4 +130,8 @@ bool NVSHandleSimple::nextEntry(nvs_opaque_iterator_t* it) { return mStoragePtr->nextEntry(it); } +const char *NVSHandleSimple::get_partition_name() const { + return mStoragePtr->getPartName(); +} + } diff --git a/components/nvs_flash/src/nvs_handle_simple.hpp b/components/nvs_flash/src/nvs_handle_simple.hpp index e05aaf7386..0f3a407fdc 100644 --- a/components/nvs_flash/src/nvs_handle_simple.hpp +++ b/components/nvs_flash/src/nvs_handle_simple.hpp @@ -76,6 +76,8 @@ public: bool nextEntry(nvs_opaque_iterator_t *it); + const char *get_partition_name() const; + private: /** * The underlying storage's object. diff --git a/components/nvs_flash/test_nvs_host/test_nvs.cpp b/components/nvs_flash/test_nvs_host/test_nvs.cpp index 279ecd2961..ded6a74d3a 100644 --- a/components/nvs_flash/test_nvs_host/test_nvs.cpp +++ b/components/nvs_flash/test_nvs_host/test_nvs.cpp @@ -533,7 +533,7 @@ TEST_CASE("erase operations are distributed among sectors", "[nvs]") SpiFlashEmulator emu(sectors); Storage storage; CHECK(storage.init(0, sectors) == ESP_OK); - + /* Fill some part of storage with static values */ const size_t static_sectors = 2; for (size_t i = 0; i < static_sectors * Page::ENTRY_COUNT; ++i) { @@ -732,8 +732,9 @@ TEST_CASE("nvs api tests", "[nvs]") CHECK(0 == strcmp(buf, str)); nvs_close(handle_1); nvs_close(handle_2); -} + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); +} TEST_CASE("nvs iterators tests", "[nvs]") { @@ -893,6 +894,8 @@ TEST_CASE("nvs iterators tests", "[nvs]") nvs_close(handle_1); nvs_close(handle_2); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("Iterator with not matching type iterates correctly", "[nvs]") @@ -927,6 +930,8 @@ TEST_CASE("Iterator with not matching type iterates correctly", "[nvs]") it = nvs_entry_find(NVS_DEFAULT_PART_NAME, NAMESPACE, NVS_TYPE_STR); CHECK(it != NULL); nvs_release_iterator(it); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("wifi test", "[nvs]") @@ -934,7 +939,6 @@ TEST_CASE("wifi test", "[nvs]") SpiFlashEmulator emu(10); emu.randomize(10); - const uint32_t NVS_FLASH_SECTOR = 5; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); @@ -1068,6 +1072,7 @@ TEST_CASE("wifi test", "[nvs]") s_perf << "Time to simulate nvs init with wifi libs: " << emu.getTotalTime() << " us (" << emu.getEraseOps() << "E " << emu.getWriteOps() << "W " << emu.getReadOps() << "R " << emu.getWriteBytes() << "Wb " << emu.getReadBytes() << "Rb)" << std::endl; + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("writing the identical content does not write or erase", "[nvs]") @@ -1121,6 +1126,8 @@ TEST_CASE("writing the identical content does not write or erase", "[nvs]") nvs_set_blob(misc_handle, "test_blob", blob, sizeof(blob)); CHECK(emu.getWriteOps() != 0); CHECK(emu.getReadOps() != 0); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("can init storage from flash with random contents", "[nvs]") @@ -1140,6 +1147,8 @@ TEST_CASE("can init storage from flash with random contents", "[nvs]") if (nvs_get_u8(handle, "wifi.opmode", &opmode) != ESP_OK) { TEST_ESP_OK(nvs_set_u8(handle, "wifi.opmode", opmode)); } + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } @@ -1194,6 +1203,8 @@ TEST_CASE("nvs api tests, starting with random data in flash", "[nvs][long]") nvs_close(handle_2); } nvs_close(handle_1); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } } extern "C" void nvs_dump(const char *partName); @@ -1477,6 +1488,8 @@ TEST_CASE("monkey test", "[nvs][monkey]") CHECK(test.doRandomThings(handle, gen, count) == ESP_OK); s_perf << "Monkey test: nErase=" << emu.getEraseOps() << " nWrite=" << emu.getWriteOps() << std::endl; + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]") @@ -1534,6 +1547,8 @@ TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]") } nvs_close(handle); totalOps = emu.getEraseOps() + emu.getWriteBytes() / 4; + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } } TEST_CASE("test for memory leaks in open/set", "[leaks]") @@ -1552,6 +1567,8 @@ TEST_CASE("test for memory leaks in open/set", "[leaks]") TEST_ESP_OK(nvs_commit(light_handle)); nvs_close(light_handle); } + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("duplicate items are removed", "[nvs][dupes]") @@ -1724,6 +1741,8 @@ TEST_CASE("read/write failure (TW8406)", "[nvs]") } nvs_close(light_handle); } + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("nvs_flash_init checks for an empty page", "[nvs]") @@ -1745,6 +1764,8 @@ TEST_CASE("nvs_flash_init checks for an empty page", "[nvs]") // first two pages are now full, third one is writable, last two are empty // init should fail TEST_ESP_ERR( nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3), ESP_ERR_NVS_NO_FREE_PAGES ); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("multiple partitions access check", "[nvs]") @@ -1762,6 +1783,9 @@ TEST_CASE("multiple partitions access check", "[nvs]") TEST_ESP_OK( nvs_get_i32(handle2, "foo", &v2)); CHECK(v1 == 0xdeadbeef); CHECK(v2 == 0xcafebabe); + + TEST_ESP_OK(nvs_flash_deinit_partition("nvs1")); + TEST_ESP_OK(nvs_flash_deinit_partition("nvs2")); } TEST_CASE("nvs page selection takes into account free entries also not just erased entries", "[nvs]") @@ -1783,6 +1807,8 @@ TEST_CASE("nvs page selection takes into account free entries also not just eras TEST_ESP_OK( nvs_set_blob(handle, "3a", blob, 4) ); TEST_ESP_OK( nvs_commit(handle) ); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("calculate used and free space", "[nvs]") @@ -1918,6 +1944,8 @@ TEST_CASE("calculate used and free space", "[nvs]") CHECK(stat1.used_entries == (h1_count_entries + h2_count_entries + h3_count_entries + stat1.namespace_count)); nvs_close(handle_3); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("Recovery from power-off when the entry being erased is not on active page", "[nvs]") @@ -1946,6 +1974,8 @@ TEST_CASE("Recovery from power-off when the entry being erased is not on active TEST_ESP_OK( nvs_get_blob(handle, "1b", blob, &read_size)); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("Recovery from power-off when page is being freed.", "[nvs]") @@ -1987,6 +2017,8 @@ TEST_CASE("Recovery from power-off when page is being freed.", "[nvs]") TEST_ESP_OK(nvs_commit(handle)); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("Multi-page blobs are supported", "[nvs]") @@ -2000,6 +2032,8 @@ TEST_CASE("Multi-page blobs are supported", "[nvs]") TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, blob_size)); TEST_ESP_OK(nvs_commit(handle)); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("Failures are handled while storing multi-page blobs", "[nvs]") @@ -2014,6 +2048,8 @@ TEST_CASE("Failures are handled while storing multi-page blobs", "[nvs]") TEST_ESP_OK(nvs_set_blob(handle, "abc", blob, Page::CHUNK_MAX_SIZE*2)); TEST_ESP_OK(nvs_commit(handle)); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("Reading multi-page blobs", "[nvs]") @@ -2033,6 +2069,8 @@ TEST_CASE("Reading multi-page blobs", "[nvs]") CHECK(memcmp(blob, blob_read, blob_size) == 0); TEST_ESP_OK(nvs_commit(handle)); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("Modification of values for Multi-page blobs are supported", "[nvs]") @@ -2061,6 +2099,8 @@ TEST_CASE("Modification of values for Multi-page blobs are supported", "[nvs]") CHECK(memcmp(blob4, blob_read, blob_size) == 0); TEST_ESP_OK( nvs_commit(handle) ); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("Modification from single page blob to multi-page", "[nvs]") @@ -2079,6 +2119,8 @@ TEST_CASE("Modification from single page blob to multi-page", "[nvs]") CHECK(memcmp(blob, blob_read, blob_size) == 0); TEST_ESP_OK(nvs_commit(handle) ); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("Modification from multi-page to single page", "[nvs]") @@ -2098,6 +2140,8 @@ TEST_CASE("Modification from multi-page to single page", "[nvs]") CHECK(memcmp(blob, blob_read, Page::CHUNK_MAX_SIZE) == 0); TEST_ESP_OK(nvs_commit(handle) ); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("Multi-page blob erased using nvs_erase_key should not be found when probed for just length", "[nvs]") @@ -2114,6 +2158,8 @@ TEST_CASE("Multi-page blob erased using nvs_erase_key should not be found when p TEST_ESP_ERR(nvs_get_blob(handle, "abc", NULL, &read_size), ESP_ERR_NVS_NOT_FOUND); TEST_ESP_OK(nvs_commit(handle)); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } @@ -2167,6 +2213,8 @@ TEST_CASE("nvs blob fragmentation test", "[nvs]") TEST_ESP_OK( nvs_set_u32(h, seq_buf, i) ); } free(blob); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("nvs code handles errors properly when partition is near to full", "[nvs]") @@ -2202,6 +2250,8 @@ TEST_CASE("Check for nvs version incompatibility", "[nvs]") TEST_ESP_OK(p.writeItem(1, ItemType::I32, "foo", &val1, sizeof(val1))); TEST_ESP_ERR(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3), ESP_ERR_NVS_NEW_VERSION_FOUND); + + nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME); } TEST_CASE("Check that NVS supports old blob format without blob index", "[nvs]") @@ -2247,6 +2297,7 @@ TEST_CASE("Check that NVS supports old blob format without blob index", "[nvs]") TEST_ESP_OK( nvs_get_blob(handle, "dummyBase64Key", buf, &buflen)); CHECK(memcmp(buf, base64data, buflen) == 0); + TEST_ESP_OK(nvs_flash_deinit_partition("test")); } TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]") @@ -2337,6 +2388,8 @@ TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]") } s_perf << "Monkey test: nErase=" << emu.getEraseOps() << " nWrite=" << emu.getWriteOps() << std::endl; + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("Recovery from power-off during modification of blob present in old-format (same page)", "[nvs]") @@ -2389,6 +2442,7 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo p2.load(0); TEST_ESP_ERR(p2.findItem(1, ItemType::BLOB, "singlepage"), ESP_ERR_NVS_TYPE_MISMATCH); + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } TEST_CASE("Recovery from power-off during modification of blob present in old-format (different page)", "[nvs]") @@ -2445,6 +2499,8 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo Page p3; p3.load(0); TEST_ESP_ERR(p3.findItem(1, ItemType::BLOB, "singlepage"), ESP_ERR_NVS_NOT_FOUND); + + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); } static void check_nvs_part_gen_args(char const *part_name, int size, char const *filename, bool is_encr, nvs_sec_cfg_t* xts_cfg) @@ -2510,6 +2566,8 @@ static void check_nvs_part_gen_args(char const *part_name, int size, char const file.close(); nvs_close(handle); + + TEST_ESP_OK(nvs_flash_deinit_partition(part_name)); } @@ -2550,8 +2608,6 @@ TEST_CASE("check and read data from partition generated via partition generation SpiFlashEmulator emu("../nvs_partition_generator/partition_single_page.bin"); - TEST_ESP_OK(nvs_flash_deinit()); - check_nvs_part_gen_args("test", 3, "../nvs_partition_generator/testdata/sample_singlepage_blob.bin", false, NULL); childpid = fork(); @@ -2914,8 +2970,8 @@ TEST_CASE("test nvs apis with encryption enabled", "[nvs]") CHECK(0 == strcmp(buf, str)); nvs_close(handle_1); nvs_close(handle_2); - TEST_ESP_OK(nvs_flash_deinit()); + TEST_ESP_OK(nvs_flash_deinit()); } TEST_CASE("test nvs apis for nvs partition generator utility with encryption enabled", "[nvs_part_gen]") @@ -3086,7 +3142,6 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena fclose(fp); - TEST_ESP_OK(nvs_flash_deinit()); nvs_sec_cfg_t cfg; for(int count = 0; count < NVS_KEY_SIZE; count++) { @@ -3154,7 +3209,6 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena fclose(fp); - TEST_ESP_OK(nvs_flash_deinit()); nvs_sec_cfg_t cfg; for(int count = 0; count < NVS_KEY_SIZE; count++) { @@ -3253,8 +3307,6 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit SpiFlashEmulator emu1("../../../tools/mass_mfg/host_test/bin/Test-1.bin"); - TEST_ESP_OK(nvs_flash_deinit()); - nvs_sec_cfg_t cfg; for(int count = 0; count < NVS_KEY_SIZE; count++) { cfg.eky[count] = 0x11; @@ -3265,8 +3317,6 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit SpiFlashEmulator emu2("../nvs_partition_generator/Test-1-partition-encrypted.bin"); - TEST_ESP_OK(nvs_flash_deinit()); - check_nvs_part_gen_args(NVS_DEFAULT_PART_NAME, 4, "testdata/sample_multipage_blob.bin", true, &cfg); @@ -3376,8 +3426,6 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit fclose(fp); - TEST_ESP_OK(nvs_flash_deinit()); - nvs_sec_cfg_t cfg; for(int count = 0; count < NVS_KEY_SIZE; count++) { @@ -3389,8 +3437,6 @@ TEST_CASE("check and read data from partition generated via manufacturing utilit SpiFlashEmulator emu2("../nvs_partition_generator/Test-1-partition-encrypted.bin"); - TEST_ESP_OK(nvs_flash_deinit()); - check_nvs_part_gen_args(NVS_DEFAULT_PART_NAME, 4, "testdata/sample_multipage_blob.bin", true, &cfg); childpid = fork(); diff --git a/components/nvs_flash/test_nvs_host/test_nvs_initialization.cpp b/components/nvs_flash/test_nvs_host/test_nvs_initialization.cpp index 15324d687b..deb4b1f53a 100644 --- a/components/nvs_flash/test_nvs_host/test_nvs_initialization.cpp +++ b/components/nvs_flash/test_nvs_host/test_nvs_initialization.cpp @@ -20,6 +20,8 @@ using namespace nvs; +#define TEST_ESP_OK(rc) CHECK((rc) == ESP_OK) + TEST_CASE("nvs_flash_init_partition_ptr fails due to nullptr arg", "[nvs_custom_part]") { const uint32_t NVS_FLASH_SECTOR = 6; @@ -61,4 +63,45 @@ TEST_CASE("nvs_flash_init_partition_ptr inits one partition", "[nvs_custom_part] CHECK(NVSPartitionManager::get_instance()->deinit_partition("test") == ESP_OK); } +TEST_CASE("deinit partition doesn't affect other partition's open handles", "[nvs]") +{ + const char *OTHER_PARTITION_NAME = "other_part"; + const uint32_t NVS_FLASH_SECTOR = 6; + const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3; + SpiFlashEmulator emu_default(10); + emu_default.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + esp_partition_t part_default = {}; + strcpy(part_default.label, NVS_DEFAULT_PART_NAME); + part_default.address = NVS_FLASH_SECTOR * SPI_FLASH_SEC_SIZE; + part_default.size = NVS_FLASH_SECTOR_COUNT_MIN * SPI_FLASH_SEC_SIZE; + + SpiFlashEmulator emu_other(10); + emu_other.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); + esp_partition_t part_other = {}; + strcpy(part_other.label, OTHER_PARTITION_NAME); + part_other.address = NVS_FLASH_SECTOR * SPI_FLASH_SEC_SIZE; + part_other.size = NVS_FLASH_SECTOR_COUNT_MIN * SPI_FLASH_SEC_SIZE; + + const char* str = "value 0123456789abcdef0123456789abcdef"; + const uint8_t blob[8] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}; + + nvs_handle_t handle_1; + + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(NVS_DEFAULT_PART_NAME, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); + TEST_ESP_OK(NVSPartitionManager::get_instance()->init_custom(OTHER_PARTITION_NAME, + NVS_FLASH_SECTOR, + NVS_FLASH_SECTOR_COUNT_MIN)); + + TEST_ESP_OK(nvs_open_from_partition(OTHER_PARTITION_NAME, "ns", NVS_READWRITE, &handle_1)); + + // Deinitializing must not interfere with the open handle from the other partition. + TEST_ESP_OK(nvs_flash_deinit_partition(NVS_DEFAULT_PART_NAME)); + + TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x3456789a)); + nvs_close(handle_1); + + TEST_ESP_OK(nvs_flash_deinit_partition(OTHER_PARTITION_NAME)); +}